-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,180 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
//! Timer V2 example | ||
//! WIP | ||
#![no_std] | ||
#![no_main] | ||
|
||
use atsame54_xpro as bsp; | ||
use bsp::hal; | ||
use hal::clock::v2 as clock; | ||
use hal::clock::v2::gclk::Gclk1Id; | ||
use hal::clock::v2::types::*; | ||
// TODO: Prelude enforces import of `fugit::ExtU32` that ruins everything.. | ||
// Temporarily remove `crate::prelude::*` import and take what's needed. | ||
use hal::ehal::digital::v2::OutputPin as _; | ||
use hal::eic::pin::*; | ||
use hal::fugit::ExtU64; | ||
use hal::gpio::{Interrupt as GpioInterrupt, *}; | ||
use hal::timer::v2::*; | ||
|
||
use panic_rtt_target as _; | ||
use rtt_target::{rprintln, rtt_init_print}; | ||
|
||
type Button = ExtInt15<Pin<PB31, GpioInterrupt<PullUp>>>; | ||
|
||
#[rtic::app(device = hal::pac, peripherals = true, dispatchers = [FREQM])] | ||
mod app { | ||
use super::*; | ||
|
||
#[monotonic(binds = TC0, default = true)] | ||
type Mono = MonotonicTimer<32, Tc0Tc1, Gclk1Id>; | ||
|
||
#[shared] | ||
struct Shared {} | ||
|
||
#[local] | ||
struct Local { | ||
button: Button, | ||
led: bsp::Led, | ||
} | ||
|
||
#[init] | ||
fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) { | ||
rtt_init_print!(); | ||
rprintln!("Application up!"); | ||
|
||
let (mut buses, clocks, tokens) = clock::clock_system_at_reset( | ||
ctx.device.OSCCTRL, | ||
ctx.device.OSC32KCTRL, | ||
ctx.device.GCLK, | ||
ctx.device.MCLK, | ||
&mut ctx.device.NVMCTRL, | ||
); | ||
|
||
let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() }; | ||
|
||
let pins = bsp::Pins::new(ctx.device.PORT); | ||
|
||
let (pclk_eic, _gclk0) = clock::pclk::Pclk::enable(tokens.pclks.eic, clocks.gclk0); | ||
|
||
let mut eic = hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.EIC); | ||
let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei(); | ||
eic.button_debounce_pins(&[button.id()]); | ||
button.sense(&mut eic, Sense::FALL); | ||
button.enable_interrupt(&mut eic); | ||
eic.finalize(); | ||
|
||
let (osculp32k, _) = | ||
clock::osculp32k::OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); | ||
|
||
let (gclk1, _osculp32k) = clock::gclk::Gclk::from_source(tokens.gclks.gclk1, osculp32k); | ||
let gclk1 = gclk1.enable(); | ||
|
||
let tc0_clk = buses.apb.enable(tokens.apbs.tc0); | ||
let tc1_clk = buses.apb.enable(tokens.apbs.tc1); | ||
|
||
let (tc0_tc1_pclk, _gclk1) = clock::pclk::Pclk::enable(tokens.pclks.tc0_tc1, gclk1); | ||
|
||
let mono = Timer::paired( | ||
ctx.device.TC0, | ||
tc0_clk, | ||
ctx.device.TC1, | ||
tc1_clk, | ||
tc0_tc1_pclk, | ||
) | ||
.into_32_bit() | ||
.into_monotonic() | ||
.unwrap(); | ||
|
||
let led: bsp::Led = bsp::pin_alias!(pins.led).into(); | ||
bump_activity_led(); | ||
|
||
(Shared {}, Local { button, led }, init::Monotonics(mono)) | ||
} | ||
|
||
#[task(binds = EIC_EXTINT_15, local = [button])] | ||
fn button(ctx: button::Context) { | ||
ctx.local.button.clear_interrupt(); | ||
|
||
rprintln!("Button pressed!"); | ||
hello::spawn().unwrap(); | ||
hello::spawn_after(50.millis()).unwrap(); | ||
hello::spawn_after(100.millis()).unwrap(); | ||
hello::spawn_after(500.millis()).unwrap(); | ||
hello::spawn_after(1.secs()).unwrap(); | ||
hello::spawn_after(5.secs()).unwrap(); | ||
} | ||
|
||
#[task(capacity = 30)] | ||
fn hello(_: hello::Context) { | ||
bump_activity_led(); | ||
rprintln!( | ||
"Hello at {} ms since epoch!", | ||
monotonics::now().duration_since_epoch().to_millis() | ||
); | ||
} | ||
|
||
#[task(local = [led])] | ||
fn activity_led(ctx: activity_led::Context, led_on: bool) { | ||
let _ = ctx.local.led.set_state((!led_on).into()); | ||
if led_on { | ||
let _ = activity_led::spawn_after(100.millis(), false); | ||
} | ||
} | ||
|
||
fn bump_activity_led() { | ||
let _ = activity_led::spawn(true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,180 +1,9 @@ | ||
//! Working with timer counter hardware | ||
use crate::ehal::timer::{CountDown, Periodic}; | ||
use crate::pac::tc0::COUNT16; | ||
use crate::pac::{MCLK, TC2, TC3}; | ||
#[cfg(all(feature = "has-tc4", feature = "has-tc5"))] | ||
use crate::pac::{TC4, TC5}; | ||
use crate::timer_params::TimerParams; | ||
use crate::timer_traits::InterruptDrivenTimer; | ||
//! # Timer | ||
//! | ||
//! Users are encouraged to use [`v2`] variant of an API because of the richer | ||
//! feature set and safety. | ||
use crate::clock; | ||
use crate::time::{Hertz, Nanoseconds}; | ||
use void::Void; | ||
pub mod v1; | ||
pub use v1::*; | ||
|
||
// Note: | ||
// TC3 + TC4 can be paired to make a 32-bit counter | ||
// TC5 + TC6 can be paired to make a 32-bit counter | ||
|
||
/// A generic hardware timer counter. | ||
/// The counters are exposed in 16-bit mode only. | ||
/// The hardware allows configuring the 8-bit mode | ||
/// and pairing up some instances to run in 32-bit | ||
/// mode, but that functionality is not currently | ||
/// exposed by this hal implementation. | ||
/// TimerCounter implements both the `Periodic` and | ||
/// the `CountDown` embedded_hal timer traits. | ||
/// Before a hardware timer can be used, it must first | ||
/// have a clock configured. | ||
pub struct TimerCounter<TC> { | ||
freq: Hertz, | ||
tc: TC, | ||
} | ||
|
||
/// This is a helper trait to make it easier to make most of the | ||
/// TimerCounter impl generic. It doesn't make too much sense to | ||
/// to try to implement this trait outside of this module. | ||
pub trait Count16 { | ||
fn count_16(&self) -> &COUNT16; | ||
} | ||
|
||
impl<TC> Periodic for TimerCounter<TC> {} | ||
impl<TC> CountDown for TimerCounter<TC> | ||
where | ||
TC: Count16, | ||
{ | ||
type Time = Nanoseconds; | ||
|
||
fn start<T>(&mut self, timeout: T) | ||
where | ||
T: Into<Self::Time>, | ||
{ | ||
let params = TimerParams::new_us(timeout.into(), self.freq); | ||
let divider = params.divider; | ||
let cycles = params.cycles; | ||
let count = self.tc.count_16(); | ||
|
||
// Disable the timer while we reconfigure it | ||
count.ctrla.modify(|_, w| w.enable().clear_bit()); | ||
while count.status.read().perbufv().bit_is_set() {} | ||
|
||
// Now that we have a clock routed to the peripheral, we | ||
// can ask it to perform a reset. | ||
count.ctrla.write(|w| w.swrst().set_bit()); | ||
|
||
while count.status.read().perbufv().bit_is_set() {} | ||
// the SVD erroneously marks swrst as write-only, so we | ||
// need to manually read the bit here | ||
while count.ctrla.read().bits() & 1 != 0 {} | ||
|
||
count.ctrlbset.write(|w| { | ||
// Count up when the direction bit is zero | ||
w.dir().clear_bit(); | ||
// Periodic | ||
w.oneshot().clear_bit() | ||
}); | ||
|
||
// Set TOP value for mfrq mode | ||
count.cc[0].write(|w| unsafe { w.cc().bits(cycles as u16) }); | ||
|
||
// Enable Match Frequency Waveform generation | ||
count.wave.modify(|_, w| w.wavegen().mfrq()); | ||
|
||
count.ctrla.modify(|_, w| { | ||
match divider { | ||
1 => w.prescaler().div1(), | ||
2 => w.prescaler().div2(), | ||
4 => w.prescaler().div4(), | ||
8 => w.prescaler().div8(), | ||
16 => w.prescaler().div16(), | ||
64 => w.prescaler().div64(), | ||
256 => w.prescaler().div256(), | ||
1024 => w.prescaler().div1024(), | ||
_ => unreachable!(), | ||
}; | ||
w.enable().set_bit(); | ||
w.runstdby().set_bit() | ||
}); | ||
} | ||
|
||
fn wait(&mut self) -> nb::Result<(), Void> { | ||
let count = self.tc.count_16(); | ||
if count.intflag.read().ovf().bit_is_set() { | ||
// Writing a 1 clears the flag | ||
count.intflag.modify(|_, w| w.ovf().set_bit()); | ||
Ok(()) | ||
} else { | ||
Err(nb::Error::WouldBlock) | ||
} | ||
} | ||
} | ||
|
||
impl<TC> InterruptDrivenTimer for TimerCounter<TC> | ||
where | ||
TC: Count16, | ||
{ | ||
/// Enable the interrupt generation for this hardware timer. | ||
/// This method only sets the clock configuration to trigger | ||
/// the interrupt; it does not configure the interrupt controller | ||
/// or define an interrupt handler. | ||
fn enable_interrupt(&mut self) { | ||
self.tc.count_16().intenset.write(|w| w.ovf().set_bit()); | ||
} | ||
|
||
/// Disables interrupt generation for this hardware timer. | ||
/// This method only sets the clock configuration to prevent | ||
/// triggering the interrupt; it does not configure the interrupt | ||
/// controller. | ||
fn disable_interrupt(&mut self) { | ||
self.tc.count_16().intenclr.write(|w| w.ovf().set_bit()); | ||
} | ||
} | ||
|
||
macro_rules! tc { | ||
($($TYPE:ident: ($TC:ident, $mclk:ident, $clock:ident, $apmask:ident),)+) => { | ||
$( | ||
pub type $TYPE = TimerCounter<$TC>; | ||
|
||
impl Count16 for $TC { | ||
fn count_16(&self) -> &COUNT16 { | ||
self.count16() | ||
} | ||
} | ||
|
||
impl TimerCounter<$TC> | ||
{ | ||
/// Configure this timer counter instance. | ||
/// The clock is obtained from the `GenericClockController` instance | ||
/// and its frequency impacts the resolution and maximum range of | ||
/// the timeout values that can be passed to the `start` method. | ||
/// Note that some hardware timer instances share the same clock | ||
/// generator instance and thus will be clocked at the same rate. | ||
pub fn $mclk(clock: &clock::$clock, tc: $TC, mclk: &mut MCLK) -> Self { | ||
// this is safe because we're constrained to just the tc3 bit | ||
mclk.$apmask.modify(|_, w| w.$mclk().set_bit()); | ||
{ | ||
let count = tc.count16(); | ||
|
||
// Disable the timer while we reconfigure it | ||
count.ctrla.modify(|_, w| w.enable().clear_bit()); | ||
while count.status.read().perbufv().bit_is_set() {} | ||
} | ||
Self { | ||
freq: clock.freq(), | ||
tc, | ||
} | ||
} | ||
} | ||
)+ | ||
} | ||
} | ||
|
||
tc! { | ||
TimerCounter2: (TC2, tc2_, Tc2Tc3Clock, apbbmask), | ||
TimerCounter3: (TC3, tc3_, Tc2Tc3Clock, apbbmask), | ||
} | ||
|
||
#[cfg(all(feature = "has-tc4", feature = "has-tc5"))] | ||
tc! { | ||
TimerCounter4: (TC4, tc4_, Tc4Tc5Clock, apbcmask), | ||
TimerCounter5: (TC5, tc5_, Tc4Tc5Clock, apbcmask), | ||
} | ||
pub mod v2; |
Oops, something went wrong.