diff --git a/boards/atsame54_xpro/Cargo.toml b/boards/atsame54_xpro/Cargo.toml index a1d5fce7f39..b6ef5ec9c49 100644 --- a/boards/atsame54_xpro/Cargo.toml +++ b/boards/atsame54_xpro/Cargo.toml @@ -38,6 +38,7 @@ rt = ["cortex-m-rt", "atsamd-hal/same54p-rt"] unproven = ["atsamd-hal/unproven"] usb = ["atsamd-hal/usb", "usb-device"] can = ["atsamd-hal/can"] +rtic = ["atsamd-hal/rtic"] [profile.dev] incremental = false @@ -52,3 +53,7 @@ opt-level = "s" [[example]] name = "mcan" required-features = ["can"] + +[[example]] +name = "timer_v2" +required-features = ["rtic"] diff --git a/boards/atsame54_xpro/examples/timer_v2.rs b/boards/atsame54_xpro/examples/timer_v2.rs new file mode 100644 index 00000000000..9170d077003 --- /dev/null +++ b/boards/atsame54_xpro/examples/timer_v2.rs @@ -0,0 +1,169 @@ +//! 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>>; + +const TIMER_HZ: u32 = 32; + +#[rtic::app(device = hal::pac, peripherals = true, dispatchers = [FREQM])] +mod app { + use super::*; + + #[monotonic(binds = TC0, default = true)] + type Mono = MonotonicTimer; + + #[shared] + struct Shared {} + + #[local] + struct Local { + countdown: CountdownTimer, + 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 monotonic = TimerBuilder::paired( + ctx.device.TC0, + tc0_clk, + ctx.device.TC1, + tc1_clk, + tc0_tc1_pclk, + ) + .into_monotonic() + .unwrap() + .enable(); + + let tc6_clk = buses.apb.enable(tokens.apbs.tc6); + let tc7_clk = buses.apb.enable(tokens.apbs.tc7); + + let (tc6_tc7_pclk, _gclk1) = clock::pclk::Pclk::enable(tokens.pclks.tc6_tc7, gclk1); + + let (_, countdown) = TimerBuilder::disjoint( + ctx.device.TC6, + tc6_clk, + ctx.device.TC7, + tc7_clk, + tc6_tc7_pclk, + ); + + // TODO: Create a PR to fugit to fix extension traits + use atsamd_hal::fugit::ExtU32; + let countdown = countdown + .into_8_bit() + .into_countdown() + .unwrap() + .with_saturated_period(1_u32.secs()) + .with_interrupt(); + + let countdown = countdown.enable(); + + let led: bsp::Led = bsp::pin_alias!(pins.led).into(); + bump_activity_led(); + + ( + Shared {}, + Local { + button, + led, + countdown, + }, + init::Monotonics(monotonic), + ) + } + #[task(binds = TC7, local = [countdown])] + fn countdown(ctx: countdown::Context) { + ctx.local.countdown.clear_interrupt_flag(); + rprintln!("Countdown timer run out!"); + bump_activity_led(); + } + + #[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); + } +} diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 153d2712253..1a2ce6af1a8 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -40,6 +40,7 @@ fugit = "0.3" modular-bitfield = "0.11" nb = "1.0" num-traits = {version = "0.2.14", default-features = false} +num-derive = "0.3" opaque-debug = "0.3.0" paste = "1.0.11" rand_core = "0.6" @@ -47,6 +48,7 @@ seq-macro = "0.3" typenum = "1.12.0" vcell = "0.1" void = {version = "1.0", default-features = false} +rtt-target = { version = "0.3", features = ["cortex-m"] } #=============================================================================== # Optional depdendencies diff --git a/hal/src/prelude.rs b/hal/src/prelude.rs index d4a5cb86918..fe980487a34 100644 --- a/hal/src/prelude.rs +++ b/hal/src/prelude.rs @@ -4,6 +4,9 @@ pub use crate::timer_traits::InterruptDrivenTimer as _atsamd_hal_timer_traits_In pub use fugit::ExtU32 as _; pub use fugit::RateExtU32 as _; +#[cfg(feature = "thumbv7")] +pub use crate::timer::v2::prelude::*; + // embedded-hal doesn’t yet have v2 in its prelude, so we need to // export it ourselves #[cfg(feature = "unproven")] diff --git a/hal/src/thumbv7em/clock/v2/apb.rs b/hal/src/thumbv7em/clock/v2/apb.rs index b8fb1d078f3..180b033d862 100644 --- a/hal/src/thumbv7em/clock/v2/apb.rs +++ b/hal/src/thumbv7em/clock/v2/apb.rs @@ -472,7 +472,7 @@ impl ApbToken { /// instances with the same [`ApbId`]. See the notes on `Token` types and /// memory safety in the root of the `clock` module for more details. #[inline] - unsafe fn new() -> Self { + pub(crate) unsafe fn new() -> Self { ApbToken { id: PhantomData } } } @@ -492,7 +492,7 @@ pub struct ApbClk { impl ApbClk { #[inline] - fn new(token: ApbToken) -> Self { + pub(crate) fn new(token: ApbToken) -> Self { ApbClk { token } } diff --git a/hal/src/thumbv7em/clock/v2/pclk.rs b/hal/src/thumbv7em/clock/v2/pclk.rs index 9845595080a..0245f374c95 100644 --- a/hal/src/thumbv7em/clock/v2/pclk.rs +++ b/hal/src/thumbv7em/clock/v2/pclk.rs @@ -105,7 +105,7 @@ impl PclkToken

{ /// instances with the same [`PclkId`]. See the notes on `Token` types and /// memory safety in the root of the `clock` module for more details. #[inline] - pub(super) unsafe fn new() -> Self { + pub(crate) unsafe fn new() -> Self { PclkToken { pclk: PhantomData } } @@ -443,7 +443,7 @@ where P: PclkId, I: PclkSourceId, { - pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { + pub(crate) fn new(token: PclkToken

, freq: Hertz) -> Self { Self { token, src: PhantomData, diff --git a/hal/src/thumbv7em/timer.rs b/hal/src/thumbv7em/timer.rs index f4c03bc9a2d..3dc8e9a3523 100644 --- a/hal/src/thumbv7em/timer.rs +++ b/hal/src/thumbv7em/timer.rs @@ -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 { - 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 Periodic for TimerCounter {} -impl CountDown for TimerCounter -where - TC: Count16, -{ - type Time = Nanoseconds; - - fn start(&mut self, timeout: T) - where - T: Into, - { - 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 InterruptDrivenTimer for TimerCounter -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; diff --git a/hal/src/thumbv7em/timer/v1.rs b/hal/src/thumbv7em/timer/v1.rs new file mode 100644 index 00000000000..f4c03bc9a2d --- /dev/null +++ b/hal/src/thumbv7em/timer/v1.rs @@ -0,0 +1,180 @@ +//! 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; + +use crate::clock; +use crate::time::{Hertz, Nanoseconds}; +use void::Void; + +// 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 { + 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 Periodic for TimerCounter {} +impl CountDown for TimerCounter +where + TC: Count16, +{ + type Time = Nanoseconds; + + fn start(&mut self, timeout: T) + where + T: Into, + { + 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 InterruptDrivenTimer for TimerCounter +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), +} diff --git a/hal/src/thumbv7em/timer/v2.rs b/hal/src/thumbv7em/timer/v2.rs new file mode 100644 index 00000000000..ac952274764 --- /dev/null +++ b/hal/src/thumbv7em/timer/v2.rs @@ -0,0 +1,179 @@ +//! # Version 2 of the `timer` module +//! +//! High level abstraction for TC (Timer/Counter) peripherals. +//! +//! Available features: +//! - Typesafe construction of all valid combination of timers +//! - Exposed configuration options like frequency/prescaler, direction, +//! interrupts etc. +//! - `rtic_monotonic::Monotonic` implementation for 32-bit configured timers +//! - Two compare registers (CC0 and CC1) that can trigger an interrupt on match +//! with a count +//! +//! Currently there is no support for counting events (only clock sources +//! through `Pclk`), capture operations and timer operates in a default, NFRQ +//! mode (normal frequency generation). +//! +//! Because of their structure, timers can be constructed through +//! - [`Timer::disjoint`] which yields a set of two timers +//! limited to 8 and 16 bit counting resolution. +//! - [`Timer::paired`] which yields a single timer capable of 8, 16 and 32 bit +//! counting resolution. +//! +//! After construction and configuration, timer has to be enabled. +//! +//! Example of how to construct, configure and enable a timer. +//! +//! ```no_run +//! # let mut pac = unsafe { atsamd_hal::pac::Peripherals::steal() }; +//! use atsamd_hal::prelude::*; +//! use atsamd_hal::clock::v2::pclk::Pclk; +//! use atsamd_hal::timer::v2 as timer; +//! use timer::{TimerBuilder, TimerPrescaler, TimerDirection, TimerInterrupt, TimerInterruptSet}; +//! +//! let (mut buses, clocks, tokens) = atsamd_hal::clock::v2::clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let gclk0 = clocks.gclk0; +//! +//! let tc0 = pac.TC0; +//! let tc1 = pac.TC1; +//! let tc2 = pac.TC2; +//! let tc3 = pac.TC3; +//! +//! let tc0_clk = buses.apb.enable(tokens.apbs.tc0); +//! let tc1_clk = buses.apb.enable(tokens.apbs.tc1); +//! let tc2_clk = buses.apb.enable(tokens.apbs.tc2); +//! let tc3_clk = buses.apb.enable(tokens.apbs.tc3); +//! +//! let (tc0_tc1_pclk, gclk0) = Pclk::enable(tokens.pclks.tc0_tc1, gclk0); +//! let (tc2_tc3_pclk, gclk0) = Pclk::enable(tokens.pclks.tc2_tc3, gclk0); +//! +//! let (pri, sec) = TimerBuilder::disjoint(tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk); /* -> (TimerBuilder, TimerBuilder) */ +//! let pri = pri +//! .into_8_bit() +//! .with_ondemand(true) +//! .into_raw() +//! .with_interrupts(TimerInterruptSet::from_iter( +//! [ +//! TimerInterrupt::Overflow, +//! TimerInterrupt::MatchOrCaptureChannel0, +//! TimerInterrupt::Error, +//! ] +//! )) +//! .with_frequency(48.MHz()) +//! .expect("oops") +//! .enable(); +//! let (tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) = (pri.disable(), sec.into_raw()).free(); +//! let timer = TimerBuilder::paired(tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk); /* -> TimerBuilder */ +//! let timer = timer /* -> RawTimer */ +//! .into_raw() +//! .with_interrupts(TimerInterrupt::Overflow.into()) +//! .with_prescaler(TimerPrescaler::DIV16) +//! .with_oneshot(true) +//! .with_direction(TimerDirection::Increment) +//! .enable(); +//! let timer = timer.disable(); +//! let timer = timer +//! .reset_into_builder() +//! .into_monotonic::<48_000_000>() +//! .expect("oops") +//! .enable(); +//! let (tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) = timer.disable().into_raw().free(); +//! +//! let (pri, sec) = TimerBuilder::disjoint(tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) /* -> (TimerBuilder, TimerBuilder) */; +//! let (tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) = (pri, sec).free(); +//! let timer = TimerBuilder::paired(tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) /* -> TimerBuilder */; +//! let mut timer = timer /* -> CountdownTimer */ +//! .into_8_bit() +//! .into_countdown::<32_768>() +//! .expect("oops") +//! .enable(); +//! timer.retrigger_with_period(2.secs()); +//! timer.enable_interrupt(); +//! timer.disable_interrupt(); +//! let (tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) = timer.into_raw().disable().free(); +//! ``` +//! +//! In order to use the timer as a RTIC monotonic timer, one can call +//! `Timer::into_monotonic`. This requires a feature flag `rtic` being enabled. + +use core::{convert::Infallible, marker::PhantomData}; + +pub mod prelude; + +mod builder; +pub use builder::*; + +mod raw; +pub use raw::*; + +#[cfg(feature = "rtic")] +mod mono; +#[cfg(feature = "rtic")] +pub use mono::*; + +mod countdown; +pub use countdown::*; + +mod interrupt; +pub use interrupt::*; + +mod types; +pub use types::*; + +use crate::{ + clock::v2::{ + apb::{ApbClk, ApbToken}, + pclk::{Pclk, PclkSourceId, PclkToken}, + }, + time::Hertz, +}; + +impl TimerBuilder { + // Create the disjoint pair of timers. They support 8-bit and 16-bit counters + pub fn disjoint( + primary_reg: T::Reg, + primary_clk: ApbClk, + secondary_reg: Reg>, + secondary_clk: ApbClk>, + common_pclk: Pclk, + ) -> (TimerBuilder, TimerBuilder, I>) { + let _ = primary_clk; + let _ = secondary_clk; + let pclk_freq = common_pclk.freq(); + let _ = common_pclk; + + ( + TimerBuilder::new(RawTimer::new(primary_reg, pclk_freq)), + TimerBuilder::new(RawTimer::new(secondary_reg, pclk_freq)), + ) + } +} + +impl TimerBuilder +where + Reg: From<(Reg, Reg>)>, +{ + // Create the paired timer. It supports 32-bit counter + pub fn paired( + primary_reg: T::Reg, + primary_clk: ApbClk, + secondary_reg: Reg>, + secondary_clk: ApbClk>, + common_pclk: Pclk, + ) -> TimerBuilder { + let _ = primary_clk; + let _ = secondary_clk; + let pclk_freq = common_pclk.freq(); + let _ = common_pclk; + TimerBuilder::new(RawTimer::new( + (primary_reg, secondary_reg).into(), + pclk_freq, + )) + } +} diff --git a/hal/src/thumbv7em/timer/v2/builder.rs b/hal/src/thumbv7em/timer/v2/builder.rs new file mode 100644 index 00000000000..58d0cda282c --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/builder.rs @@ -0,0 +1,84 @@ +use super::*; + +pub struct TimerBuilder(RawTimer); + +impl TimerBuilder { + pub(super) fn new(inner: RawTimer) -> Self { + Self(inner) + } + + pub fn into_8_bit(self) -> TimerBuilder { + self.0.reg.count8().ctrla.modify(|_, w| w.mode().count8()); + TimerBuilder(self.0.to()) + } + + pub fn into_16_bit(self) -> TimerBuilder { + self.0.reg.count8().ctrla.modify(|_, w| w.mode().count16()); + TimerBuilder(self.0.to()) + } + + pub fn with_ondemand(mut self, value: bool) -> Self { + self.0.set_ondemand(value); + self + } + + pub fn with_runstdby(mut self, value: bool) -> Self { + self.0.set_runstdby(value); + self + } + + pub fn with_debug_run(mut self, value: bool) -> Self { + self.0.set_debug_run(value); + self + } +} + +impl IntoRaw for TimerBuilder { + fn into_raw(self) -> RawTimer { + self.0 + } +} + +impl TimerBuilder { + pub fn into_32_bit(self) -> TimerBuilder { + self.0.reg.count8().ctrla.modify(|_, w| w.mode().count32()); + TimerBuilder(self.0.to()) + } +} + +impl FreeTimerResourcesExt + for TimerBuilder +where + T::Reg: Into<(Reg, Reg)>, +{ + fn free( + self, + ) -> ( + Reg, + ApbClk, + Reg, + ApbClk, + Pclk, + ) { + self.0.free() + } +} + +impl FreeTimerResourcesExt + for ( + TimerBuilder, + TimerBuilder, I, TW2>, + ) +{ + fn free( + self, + ) -> ( + T::Reg, + ApbClk, + Reg>, + ApbClk>, + Pclk, + ) { + (self.0 .0, self.1 .0).free() + } +} diff --git a/hal/src/thumbv7em/timer/v2/countdown.rs b/hal/src/thumbv7em/timer/v2/countdown.rs new file mode 100644 index 00000000000..e99d60b254f --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/countdown.rs @@ -0,0 +1,170 @@ +use super::*; + +pub trait IntoCountdown { + fn into_countdown( + self, + ) -> Result, TimerError>; +} + +impl IntoCountdown + for TimerBuilder +{ + fn into_countdown( + self, + ) -> Result, TimerError> { + let raw = self + .into_raw() + .with_frequency(Hertz::Hz(TIMER_HZ))? + .with_mode(TimerMode::MFRQ) + .with_direction(TimerDirection::Decrement) + .with_interrupts(TimerInterruptSet::empty()); + Ok(UnreadyCountdownTimer(CountdownTimer { raw })) + } +} + +pub struct UnreadyCountdownTimer( + CountdownTimer, +); + +impl + UnreadyCountdownTimer +{ + pub fn with_period( + mut self, + period: fugit::TimerDurationU32, + ) -> Result, CountdownTimerError> { + self.0.set_period(period)?; + Ok(self.0) + } + + pub fn with_saturated_period( + mut self, + period: fugit::TimerDurationU32, + ) -> CountdownTimer { + self.0.set_saturated_period(period); + self.0 + } +} + +pub struct CountdownTimer { + raw: RawTimer, +} + +#[derive(Copy, Clone, Debug)] +pub enum CountdownTimerError { + OutOfRange, +} + +impl + CountdownTimer +{ + pub fn enable(self) -> CountdownTimer { + CountdownTimer { + raw: self.raw.enable(), + } + } + + pub fn with_oneshot(mut self, value: bool) -> Self { + self.set_oneshot(value); + self + } + + pub fn with_interrupt(mut self) -> Self { + self.enable_interrupt(); + self + } +} + +impl + CountdownTimer +{ + pub fn disable(self) -> CountdownTimer { + CountdownTimer { + raw: self.raw.disable(), + } + } + + pub fn retrigger(&mut self) -> &mut Self { + self.raw.retrigger(); + self + } + + pub fn wait(&mut self) -> nb::Result<(), Infallible> { + if self.raw.interrupt_flags().overflow() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + pub fn clear_interrupt_flag(&mut self) -> &mut Self { + let _ = self.raw.interrupt_flags(); + self + } +} + +impl + CountdownTimer +{ + // TODO: set_period -> try_set_period ? Provide guaranteed `set_period` for u32 + // timer Or maybe just set_saturating_period to avoid all that hassle + pub fn set_period( + &mut self, + period: fugit::TimerDurationU32, + ) -> Result<&mut Self, CountdownTimerError> { + match TW::from_u32(period.ticks()) { + Some(ticks) => { + self.raw.set_compare(TimerCompareRegister::Zero, ticks); + self.raw.set_count(ticks); + Ok(self) + } + None => Err(CountdownTimerError::OutOfRange), + } + } + + pub fn set_saturated_period(&mut self, period: fugit::TimerDurationU32) -> &mut Self { + let period = match TW::from_u32(period.ticks() - 1) { + Some(ticks) => ticks, + None => TW::max_value(), + }; + self.raw.set_compare(TimerCompareRegister::Zero, period); + self.raw.set_count(period); + self + } + + pub fn set_oneshot(&mut self, value: bool) -> &mut Self { + self.raw.set_oneshot(value); + self + } + + pub fn enable_interrupt(&mut self) -> &mut Self { + self.raw.enable_interrupts(TimerInterrupt::Overflow.into()); + self + } + + pub fn disable_interrupt(&mut self) -> &mut Self { + self.raw.disable_interrupts(TimerInterrupt::Overflow.into()); + self + } + + // TODO: Clean it up + /// # Safety + /// API of the `CountdownTimer` full ownership over underlying timer and + /// makes assumptions about its state + /// - direction: decrement + /// - interrupts: all but overflow should be disabled; overflow is _do not + /// care_ + /// - mode: mfrq (must wrap on CC0, NOT on TOP) + /// - frequency: matches TIMER_HZ + pub unsafe fn inner_raw(&mut self) -> &mut RawTimer { + &mut self.raw + } +} + +impl IntoRaw + for CountdownTimer +{ + fn into_raw(self) -> RawTimer { + self.raw + } +} diff --git a/hal/src/thumbv7em/timer/v2/interrupt.rs b/hal/src/thumbv7em/timer/v2/interrupt.rs new file mode 100644 index 00000000000..6b469c46e81 --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/interrupt.rs @@ -0,0 +1,103 @@ +use num_traits::FromPrimitive; + +#[repr(u8)] +#[derive(Copy, Clone, PartialEq, Debug, Eq, num_derive::FromPrimitive)] +pub enum TimerInterrupt { + MatchOrCaptureChannel1 = 1 << 5, + MatchOrCaptureChannel0 = 1 << 4, + Error = 1 << 1, + Overflow = 1 << 0, +} + +bitfield::bitfield! { + /// Raw userpage POD struct that exposes bitfields via methods + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct TimerInterruptSet(u8); + impl Debug; + u8; + pub overflow, set_overflow: 0; + pub error, set_error: 1; + pub match_or_capture_channel_0, set_match_or_capture_channel_0: 4; + pub match_or_capture_channel_1, set_match_or_capture_channel_1: 5; +} + +impl TimerInterruptSet { + pub fn full() -> Self { + Self::from_iter(Self(u8::MAX)) + } + + pub fn empty() -> Self { + Self(0) + } +} + +impl From for TimerInterruptSet { + fn from(value: TimerInterrupt) -> Self { + Self(value as _) + } +} + +impl FromIterator for TimerInterruptSet { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().fold(0, |l, r| l | r as u8)) + } +} + +impl Iterator for TimerInterruptSet { + type Item = TimerInterrupt; + + fn next(&mut self) -> Option { + let raw_interrupt_set = &mut self.0; + while *raw_interrupt_set != 0 { + // Count of trailing zeros is equal to a bit index of an interrupt + let raw_interrupt = 1 << raw_interrupt_set.trailing_zeros(); + // Clear the bit + *raw_interrupt_set &= !raw_interrupt; + match TimerInterrupt::from_u8(raw_interrupt) { + None => continue, + Some(i) => return Some(i), + } + } + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_full_interrupt_set() { + let interrupts = [ + TimerInterrupt::MatchOrCaptureChannel0, + TimerInterrupt::MatchOrCaptureChannel1, + TimerInterrupt::Error, + TimerInterrupt::Overflow, + ]; + let full_interrupt_set_from_u8_max = TimerInterruptSet::full(); + let full_interrupt_set_from_summed_variants = TimerInterruptSet( + TimerInterrupt::MatchOrCaptureChannel0 as u8 + | TimerInterrupt::MatchOrCaptureChannel1 as u8 + | TimerInterrupt::Error as u8 + | TimerInterrupt::Overflow as u8, + ); + let full_interrupt_set = TimerInterruptSet::from_iter(interrupts); + assert_eq!(full_interrupt_set_from_u8_max, full_interrupt_set); + assert_eq!(full_interrupt_set_from_summed_variants, full_interrupt_set); + assert_eq!(full_interrupt_set.count(), interrupts.len()); + } + #[test] + fn test_interrupt_set_with_single_interrupts() { + for interrupt in TimerInterruptSet(u8::MAX) { + let is_from = TimerInterruptSet::from(interrupt); + let is_from_iter = TimerInterruptSet::from_iter([interrupt]); + assert_eq!(is_from, is_from_iter); + } + } + #[test] + fn test_empty_interrupt_set() { + let interrupt_set_from_0 = TimerInterruptSet(0); + let interrupt_set_from_empty_iterator = TimerInterruptSet::from_iter([]); + assert_eq!(0, interrupt_set_from_0.count()); + assert_eq!(interrupt_set_from_0, interrupt_set_from_empty_iterator); + } +} diff --git a/hal/src/thumbv7em/timer/v2/mono.rs b/hal/src/thumbv7em/timer/v2/mono.rs new file mode 100644 index 00000000000..1738a9cdff0 --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/mono.rs @@ -0,0 +1,112 @@ +use super::*; + +pub trait IntoMonotonic { + fn into_monotonic( + self, + ) -> Result, TimerError>; +} + +impl IntoMonotonic for TimerBuilder { + fn into_monotonic( + self, + ) -> Result, TimerError> { + let raw = self + .into_32_bit() + .into_raw() + .with_frequency(Hertz::Hz(TIMER_HZ))? + .with_mode(TimerMode::NFRQ) + .with_oneshot(false) + .with_direction(TimerDirection::Increment) + .with_interrupts(TimerInterrupt::Overflow.into()); + Ok(MonotonicTimer { raw, high_bits: 0 }) + } +} + +pub struct MonotonicTimer { + raw: RawTimer, + high_bits: u32, +} + +impl MonotonicTimer { + pub fn enable(self) -> MonotonicTimer { + MonotonicTimer { + raw: self.raw.enable(), + high_bits: self.high_bits, + } + } +} + +impl MonotonicTimer { + pub unsafe fn inner_raw(&mut self) -> &mut RawTimer { + &mut self.raw + } + + pub fn disable(self) -> MonotonicTimer { + MonotonicTimer { + raw: self.raw.disable(), + high_bits: self.high_bits, + } + } +} + +impl IntoRaw + for MonotonicTimer +{ + fn into_raw(self) -> RawTimer { + self.raw + } +} + +impl rtic_monotonic::Monotonic + for MonotonicTimer +{ + const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false; + + type Instant = fugit::TimerInstantU64; + + type Duration = fugit::TimerDurationU64; + + fn now(&mut self) -> Self::Instant { + let ticks = u64::from(self.high_bits) << u32::BITS | u64::from(self.raw.count()); + Self::Instant::from_ticks(ticks) + } + + fn set_compare(&mut self, instant: Self::Instant) { + // Set comparator value and enable related interrupt only if requested `instant` + // is currently in range for a timer (smaller than u32::MAX). `set_compare` will + // be attempted every time timer overflows until it is finally in range. + if instant + .checked_duration_since(self.now()) + .map_or(0, |d| d.ticks()) + <= u32::MAX.into() + { + let timer = &mut self.raw; + timer.enable_interrupts(TimerInterrupt::MatchOrCaptureChannel0.into()); + timer.set_compare(TimerCompareRegister::Zero, instant.ticks() as u32); + } + } + + fn clear_compare_flag(&mut self) { + let timer = &mut self.raw; + let interrupts = timer.interrupt_flags(); + for interrupt in interrupts { + match interrupt { + TimerInterrupt::Overflow => self.high_bits += 1, + TimerInterrupt::MatchOrCaptureChannel0 => { + timer.disable_interrupts(TimerInterrupt::MatchOrCaptureChannel0.into()); + timer.set_compare(TimerCompareRegister::Zero, 0); + } + _ => {} + } + } + } + + fn zero() -> Self::Instant { + Self::Instant::from_ticks(0) + } + + unsafe fn reset(&mut self) { + self.high_bits = 0; + self.raw.set_count(0); + } +} diff --git a/hal/src/thumbv7em/timer/v2/prelude.rs b/hal/src/thumbv7em/timer/v2/prelude.rs new file mode 100644 index 00000000000..3bdb925179b --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/prelude.rs @@ -0,0 +1,5 @@ +pub use super::FreeTimerResourcesExt as _; +pub use super::IntoCountdown as _; +#[cfg(feature = "rtic")] +pub use super::IntoMonotonic as _; +pub use super::IntoRaw as _; diff --git a/hal/src/thumbv7em/timer/v2/raw.rs b/hal/src/thumbv7em/timer/v2/raw.rs new file mode 100644 index 00000000000..11c5d362685 --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/raw.rs @@ -0,0 +1,335 @@ +use super::*; + +pub trait IntoRaw { + fn into_raw(self) -> RawTimer; +} + +// TODO: These `pub(super)` are somewhat smelly; especially `reg` and `to` +// Maybe `Builder` should have access to a better API inside Timer, maybe at +// least unsafe Or maybe they should not be sibling modules, if builder needs to +// access raw, then maybe builder should be just submodule of raw. + +/// General timer abstraction. WiP. +pub struct RawTimer { + pub(super) reg: T::Reg, // pub(super) for the builder + pclk_freq: Hertz, + __: PhantomData<(I, S, TW)>, +} + +impl RawTimer { + // pub(super) for the builder + pub(super) fn new(reg: T::Reg, pclk_freq: Hertz) -> Self { + reg.count8().ctrla.modify(|_, w| w.swrst().set_bit()); + while reg.count8().syncbusy.read().swrst().bit_is_set() {} + let mut s = Self { + reg, + pclk_freq, + __: PhantomData, + }; + s.set_debug_run(false); // Not controlled by SWRST, setting explicitly + s + } +} + +impl RawTimer { + // pub(super) for the builder + pub(super) fn to(self) -> RawTimer { + RawTimer { + reg: self.reg, + pclk_freq: self.pclk_freq, + __: PhantomData, + } + } + + fn sync_ctrlbset_write(&mut self, write: impl WriteClosure) { + self.reg.count8().ctrlbset.write(write); + while self.reg.count8().syncbusy.read().ctrlb().bit_is_set() {} + } + + fn sync_ctrlbclr_write(&mut self, write: impl WriteClosure) { + self.reg.count8().ctrlbclr.write(write); + while self.reg.count8().syncbusy.read().ctrlb().bit_is_set() {} + } + + pub fn set_debug_run(&mut self, value: bool) -> &mut Self { + self.reg.count8().dbgctrl.write(|w| w.dbgrun().bit(value)); + self + } + + // To re-engage the timer one must call [`Timer::retrigger`] + pub fn set_oneshot(&mut self, value: bool) -> &mut Self { + if value { + self.sync_ctrlbset_write(|w| w.oneshot().set_bit()); + } else { + self.sync_ctrlbclr_write(|w| w.oneshot().set_bit()); + } + self + } + + pub fn set_ondemand(&mut self, value: bool) -> &mut Self { + self.reg + .count8() + .ctrla + .modify(|_, w| w.ondemand().bit(value)); + self + } + + pub fn set_runstdby(&mut self, value: bool) -> &mut Self { + self.reg + .count8() + .ctrla + .modify(|_, w| w.runstdby().bit(value)); + self + } + + pub fn set_direction(&mut self, dir: TimerDirection) -> &mut Self { + match dir { + TimerDirection::Increment => self.sync_ctrlbclr_write(|w| w.dir().set_bit()), + TimerDirection::Decrement => self.sync_ctrlbset_write(|w| w.dir().set_bit()), + } + self + } + + pub fn direction(&self) -> TimerDirection { + match self.reg.count8().ctrlbset.read().dir().bit_is_set() { + true => TimerDirection::Decrement, + false => TimerDirection::Increment, + } + } + + pub fn enable_interrupts(&mut self, interrupt_set: TimerInterruptSet) -> &mut Self { + self.reg + .count8() + .intenset + .write(|w| unsafe { w.bits(interrupt_set.0) }); + self + } + + pub fn disable_interrupts(&mut self, interrupt_set: TimerInterruptSet) -> &mut Self { + self.reg + .count8() + .intenclr + .write(|w| unsafe { w.bits(interrupt_set.0) }); + self + } + + pub unsafe fn registers(&mut self) -> &T::Reg { + &self.reg + } + + fn sync_run_command(&mut self, cmd: TimerCommand) -> &mut Self { + self.sync_ctrlbset_write(|w| w.cmd().variant(cmd)); + while !self.reg.count8().ctrlbset.read().cmd().is_none() {} + self + } +} + +impl RawTimer { + // TODO: Restoring the builder is dangerous if new configuration in + // "raw" mode violated some invariant that other "specific timer" + // might have made. + pub unsafe fn into_builder(self) -> TimerBuilder { + TimerBuilder::new(self) + } + + pub fn reset_into_builder(self) -> TimerBuilder { + TimerBuilder::new(RawTimer::new(self.reg, self.pclk_freq)) + } + + pub fn enable(self) -> RawTimer { + self.reg.count8().ctrla.modify(|_, w| w.enable().set_bit()); + while self.reg.count8().syncbusy.read().enable().bit_is_set() {} + self.to() + } + + pub fn with_ondemand(mut self, value: bool) -> Self { + self.set_ondemand(value); + self + } + + pub fn with_runstdby(mut self, value: bool) -> Self { + self.set_runstdby(value); + self + } + + pub fn with_oneshot(mut self, value: bool) -> Self { + self.set_oneshot(value); + self + } + + pub fn with_direction(mut self, dir: TimerDirection) -> Self { + self.set_direction(dir); + self + } + + /// Disables all interrupts first + pub fn with_interrupts(mut self, interrupt_set: TimerInterruptSet) -> Self { + // TODO: If monotonic/countdown decays into raw then we should disable + // interrupts first so it's more idempotent But then, maybe + // monotonic/countdown should do the same? :thinking: + // self.disable_interrupts(InterruptSet::full()); + self.enable_interrupts(interrupt_set); + self + } + + /// Mutually exclusive with [`Self::with_prescaler`] + pub fn with_frequency(self, frequency: Hertz) -> Result { + let input_freq = self.pclk_freq.to_Hz(); + let output_freq = frequency.to_Hz(); + let prescaler = match input_freq.checked_rem(output_freq) { + Some(0) => match input_freq / output_freq { + 1 => TimerPrescaler::DIV1, + 2 => TimerPrescaler::DIV2, + 4 => TimerPrescaler::DIV4, + 8 => TimerPrescaler::DIV8, + 16 => TimerPrescaler::DIV16, + 64 => TimerPrescaler::DIV64, + 256 => TimerPrescaler::DIV256, + 1024 => TimerPrescaler::DIV1024, + _ => { + return Err(TimerError::NoValidPrescaler( + TimerPrescalerError::InvalidPrescaler, + )) + } + }, + Some(_) => { + return Err(TimerError::NoValidPrescaler( + TimerPrescalerError::FrequenciesNotDivisible, + )) + } + None => { + return Err(TimerError::NoValidPrescaler( + TimerPrescalerError::OutputFrequencyIsZero, + )) + } + }; + Ok(self.with_prescaler(prescaler)) + } + + /// Mutually exclusive with [`Self::with_frequency`] + pub fn with_prescaler(self, prescaler: TimerPrescaler) -> Self { + self.reg + .count8() + .ctrla + .modify(|_, w| w.prescaler().variant(prescaler)); + self + } + + pub fn with_mode(self, mode: TimerMode) -> Self { + self.reg.count8().wave.write(|w| w.wavegen().variant(mode)); + self + } +} + +impl RawTimer { + pub fn disable(self) -> RawTimer { + self.reg.count8().ctrla.modify(|_, w| w.enable().set_bit()); + while self.reg.count8().syncbusy.read().enable().bit_is_set() {} + self.to() + } + + pub fn retrigger(&mut self) { + self.sync_run_command(TimerCommand::RETRIGGER); + } + + // Clears the interrupts + #[must_use] + pub fn interrupt_flags(&mut self) -> TimerInterruptSet { + let interrupts = self.reg.count8().intflag.read().bits(); + // Clear the interrupts + self.reg + .count8() + .intflag + .write(|w| unsafe { w.bits(interrupts) }); + TimerInterruptSet(interrupts) + } +} + +// TODO: Make sure that count && CCs can be interacted with when timer is +// disabled as well as when enabled Especially if `sync_run_command` is fine +// when disabled +impl RawTimer { + pub fn compare(&self, index: TimerCompareRegister) -> TW { + // Compare register value must be <= TW::MAX, this is noop + TW::truncated_from_u32(self.reg.count32().cc[index as usize].read().cc().bits()) + } + + pub fn set_compare(&mut self, index: TimerCompareRegister, compare: TW) -> &mut Self { + // Writing over reserved bits for an 8/16 bit timer, o-la-la. + // Hopefully that's fine. + self.reg.count32().cc[index as usize].write(|w| unsafe { w.cc().bits(compare.as_()) }); + match index { + TimerCompareRegister::Zero => { + while self.reg.count8().syncbusy.read().cc0().bit_is_set() {} + } + TimerCompareRegister::One => { + while self.reg.count8().syncbusy.read().cc1().bit_is_set() {} + } + } + self + } + + pub fn count(&mut self) -> TW { + self.sync_run_command(TimerCommand::READSYNC); + // Count value must be <= TW::MAX, this is noop + TW::truncated_from_u32(self.reg.count32().count.read().count().bits()) + } + + pub fn set_count(&mut self, count: TW) -> &mut Self { + // Writing over reserved bits for an 8/16 bit timer, o-la-la. + // Hopefully that's fine. + self.reg + .count32() + .count + .write(|w| unsafe { w.count().bits(count.as_()) }); + while self.reg.count8().syncbusy.read().count().bit_is_set() {} + self + } +} + +impl FreeTimerResourcesExt + for (RawTimer, RawTimer, I, TW2>) +{ + fn free( + self, + ) -> ( + T::Reg, + ApbClk, + Reg>, + ApbClk>, + Pclk, + ) { + ( + self.0.reg, + ApbClk::new(unsafe { ApbToken::new() }), + self.1.reg, + ApbClk::new(unsafe { ApbToken::new() }), + Pclk::new(unsafe { PclkToken::new() }, self.0.pclk_freq), + ) + } +} + +impl FreeTimerResourcesExt + for RawTimer +where + T::Reg: Into<(Reg, Reg)>, +{ + fn free( + self, + ) -> ( + Reg, + ApbClk, + Reg, + ApbClk, + Pclk, + ) { + let (pri_reg, sec_reg) = self.reg.into(); + ( + pri_reg, + ApbClk::new(unsafe { ApbToken::new() }), + sec_reg, + ApbClk::new(unsafe { ApbToken::new() }), + Pclk::new(unsafe { PclkToken::new() }, self.pclk_freq), + ) + } +} diff --git a/hal/src/thumbv7em/timer/v2/types.rs b/hal/src/thumbv7em/timer/v2/types.rs new file mode 100644 index 00000000000..8a93b27b32b --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/types.rs @@ -0,0 +1,227 @@ +use core::ops::Deref; + +use crate::clock::v2::apb::{ApbClk, ApbId}; +use crate::clock::v2::pclk::{Pclk, PclkId, PclkSourceId}; +use crate::clock::v2::types; +use crate::pac; + +/// Any timer identifier (TimerIds + CombinedTimerIds) +pub trait AbstractTimerId { + type Reg: core::ops::Deref; +} + +/// "non-combined" timer identifier (Tc0, Tc1, ..., Tc7) +/// +/// TimerId matches 1:1 ApbId; thus subtrait relation +pub trait TimerId: ApbId + AbstractTimerId {} + +/// "combined" timer identifiers (Tc0Tc1, Tc2Tc3, ...) +/// CombinedTimerIds matches 1:1 PclkIds; thus subtrait relation +pub trait CombinedTimerId: PclkId + AbstractTimerId { + type PrimaryTimer: PrimaryTimerId; + type SecondaryTimer: TimerId; +} + +/// "primary" timers are the even-numbered ones (Tc0, Tc2, Tc4, Tc6) +/// +/// they usually drive the timer in a "paired" mode +pub trait PrimaryTimerId: TimerId { + type CombinedTimer: CombinedTimerId; +} + +/// Marker trait for "primary" timers which support "paired" mode +pub trait PairableTimerId: PrimaryTimerId {} + +macro_rules! impl_timer_traits { + ( + $( + $( #[$( $cfg:tt )+] )? + (($t1:ty, $t1_reg:ty), ($t2:ty, $t2_reg:ty), $t3:ident) + ),+$(,)? + ) => { + $( + paste::paste! { + $( #[$( $cfg )+] )? + mod [] { + use super::{ + AbstractTimerId, PrimaryTimerId, + CombinedTimerId, TimerId, CombinedTimerReg, + pac, types + }; + use pac::{$t1_reg, $t2_reg}; + use types::{$t1, $t2, $t3}; + + impl AbstractTimerId for $t1 { + type Reg = $t1_reg; + } + impl TimerId for $t1 {} + + impl PrimaryTimerId for $t1 { + type CombinedTimer = $t3; + } + + impl AbstractTimerId for $t2 { + type Reg = $t2_reg; + } + impl TimerId for $t2 { + } + + impl AbstractTimerId for $t3 { + // Allegedly when in a "paired" mode, + // HW reg access should be done through the primary timer + type Reg = CombinedTimerReg<$t1_reg, $t2_reg>; + } + impl CombinedTimerId for $t3 { + type PrimaryTimer = $t1; + type SecondaryTimer = $t2; + } + } + } + )+ + } +} + +impl_timer_traits! { + #[cfg(all(feature = "has-tc0", feature = "has-tc1"))] + ((Tc0, TC0), (Tc1, TC1), Tc0Tc1), + #[cfg(all(feature = "has-tc2", feature = "has-tc3"))] + ((Tc2, TC2), (Tc3, TC3), Tc2Tc3), + #[cfg(all(feature = "has-tc4", feature = "has-tc5"))] + ((Tc4, TC4), (Tc5, TC5), Tc4Tc5), + #[cfg(all(feature = "has-tc6", feature = "has-tc7"))] + ((Tc6, TC6), (Tc7, TC7), Tc6Tc7), +} + +#[cfg(all(feature = "has-tc0", feature = "has-tc1"))] +impl PairableTimerId for types::Tc0 {} +#[cfg(all(feature = "has-tc2", feature = "has-tc3"))] +impl PairableTimerId for types::Tc2 {} + +/// Wrapper for a combined timer (Timer) to hold +/// registers of inner timers. +/// +/// Main purpose of it is to be able to return both of them on +/// deconstruction. Did not find a way to `core::mem::transmute(())` into +/// `T::Reg` because size of a type is not known +pub struct CombinedTimerReg { + pub primary_timer_reg: P, + pub secondary_timer_reg: S, +} + +impl From<(P, S)> for CombinedTimerReg { + fn from(value: (P, S)) -> Self { + Self { + primary_timer_reg: value.0, + secondary_timer_reg: value.1, + } + } +} + +impl From> for (P, S) { + fn from(value: CombinedTimerReg) -> Self { + (value.primary_timer_reg, value.secondary_timer_reg) + } +} + +impl Deref for CombinedTimerReg { + type Target = P::Target; + + // HW register from the primary timer is used when in "paired" mode + fn deref(&self) -> &Self::Target { + self.primary_timer_reg.deref() + } +} + +pub mod state { + pub trait State {} + + pub enum Disabled {} + pub enum Enabled {} + + impl State for Disabled {} + impl State for Enabled {} +} + +pub mod timer_width { + use num_traits::*; + pub trait TimerWidth: Copy + Clone + AsPrimitive + FromPrimitive + Bounded { + fn truncated_from_u32(v: u32) -> Self; + } + + macro_rules! impl_timer_width_for { + ( + $($t:ty),+ + ) => { + $( + impl TimerWidth for $t { + fn truncated_from_u32(v: u32) -> Self { + v as Self + } + } + )+ + } + } + + impl_timer_width_for!(u8, u16, u32); +} + +#[derive(Copy, Clone, Debug)] +pub enum TimerDirection { + Increment, + Decrement, +} + +pub type TimerPrescaler = crate::pac::tc0::count8::ctrla::PRESCALER_A; + +pub type TimerMode = crate::pac::tc0::count8::wave::WAVEGEN_A; + +#[derive(Copy, Clone, Debug)] +pub enum TimerError { + NoValidPrescaler(TimerPrescalerError), +} + +#[derive(Copy, Clone, Debug)] +pub enum TimerPrescalerError { + InvalidPrescaler, + FrequenciesNotDivisible, + OutputFrequencyIsZero, +} + +pub(super) type TimerCommand = crate::pac::tc0::count8::ctrlbset::CMD_A; + +pub(super) trait WriteClosure: + FnOnce(&mut T::Writer) -> &mut crate::pac::generic::W +{ +} + +impl WriteClosure for C +where + T: crate::pac::generic::Writable, + C: FnOnce(&mut T::Writer) -> &mut crate::pac::generic::W, +{ +} + +pub(super) type CtrlBSet = crate::pac::tc0::count8::ctrlbset::CTRLBSET_SPEC; +pub(super) type CtrlBClr = crate::pac::tc0::count8::ctrlbclr::CTRLBCLR_SPEC; + +#[derive(Copy, Clone, PartialEq, Debug, Eq)] +pub enum TimerCompareRegister { + Zero = 0, + One = 1, +} + +pub(super) type SecondaryTimer = + <::CombinedTimer as CombinedTimerId>::SecondaryTimer; +pub(super) type Reg = ::Reg; + +pub trait FreeTimerResourcesExt { + fn free( + self, + ) -> ( + T::Reg, + ApbClk, + Reg>, + ApbClk>, + Pclk, + ); +}