From b458f99f568fc06ec5dd3213a2c8e8337042b4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Thu, 1 Jun 2023 17:22:52 +0200 Subject: [PATCH 1/3] Timer V2 --- boards/atsame54_xpro/Cargo.toml | 5 + boards/atsame54_xpro/examples/timer_v2.rs | 128 ++++++ hal/Cargo.toml | 2 + hal/src/thumbv7em/clock/v2/apb.rs | 4 +- hal/src/thumbv7em/clock/v2/pclk.rs | 4 +- hal/src/thumbv7em/timer.rs | 185 +-------- hal/src/thumbv7em/timer/v1.rs | 180 +++++++++ hal/src/thumbv7em/timer/v2.rs | 457 ++++++++++++++++++++++ hal/src/thumbv7em/timer/v2/interrupt.rs | 99 +++++ hal/src/thumbv7em/timer/v2/mono.rs | 86 ++++ hal/src/thumbv7em/timer/v2/types.rs | 212 ++++++++++ 11 files changed, 1180 insertions(+), 182 deletions(-) create mode 100644 boards/atsame54_xpro/examples/timer_v2.rs create mode 100644 hal/src/thumbv7em/timer/v1.rs create mode 100644 hal/src/thumbv7em/timer/v2.rs create mode 100644 hal/src/thumbv7em/timer/v2/interrupt.rs create mode 100644 hal/src/thumbv7em/timer/v2/mono.rs create mode 100644 hal/src/thumbv7em/timer/v2/types.rs diff --git a/boards/atsame54_xpro/Cargo.toml b/boards/atsame54_xpro/Cargo.toml index a1d5fce7f39a..b6ef5ec9c492 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 000000000000..44daf6257b5d --- /dev/null +++ b/boards/atsame54_xpro/examples/timer_v2.rs @@ -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>>; + +#[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); + } +} diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 153d2712253a..1a2ce6af1a89 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/thumbv7em/clock/v2/apb.rs b/hal/src/thumbv7em/clock/v2/apb.rs index b8fb1d078f39..180b033d8622 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 9845595080ad..0245f374c957 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 f4c03bc9a2d5..3dc8e9a35238 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 000000000000..f4c03bc9a2d5 --- /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 000000000000..a5fe1b8b3e5d --- /dev/null +++ b/hal/src/thumbv7em/timer/v2.rs @@ -0,0 +1,457 @@ +//! # 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 +//! # use atsamd_hal::clock::v2::pclk::Pclk; +//! # use atsamd_hal::timer::v2::{Timer, TimersTupleExt, TimerPrescaler, TimerDirection}; +//! # let mut pac = unsafe { atsamd_hal::pac::Peripherals::steal() }; +//! 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) = Timer::disjoint(tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) /* -> (Timer, Timer) */; +//! let (tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) = (pri, sec).free(); +//! let timer = Timer::paired(tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) /* -> Timer */; +//! let timer = timer +//! .with_prescaler(TimerPrescaler::DIV16) +//! .with_oneshot(true) +//! .with_direction(TimerDirection::Increment) +//! .enable(); +//! let timer = timer.disable(); +//! let (tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) = timer.free(); +//! +//! let (pri, sec) = Timer::disjoint(tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) /* -> (Timer, Timer) */; +//! let (tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) = (pri, sec).free(); +//! let timer = Timer::paired(tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) /* -> Timer */; +//! let timer = timer.into_32_bit().enable().disable(); +//! let (tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) = timer.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::marker::PhantomData; + +#[cfg(feature = "rtic")] +mod mono; +#[cfg(feature = "rtic")] +pub use mono::MonotonicTimer; + +mod interrupt; +pub use interrupt::{Interrupt, InterruptSet}; + +mod types; +pub use types::*; + +use crate::{ + clock::v2::{ + apb::{ApbClk, ApbToken}, + pclk::{Pclk, PclkSourceId, PclkToken}, + }, + time::Hertz, +}; + +/// General timer abstraction. WiP. +pub struct Timer { + reg: T::Reg, + pclk_freq: Hertz, + __: PhantomData<(I, S, TW)>, +} + +impl Timer { + 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() {} + Self { + reg, + pclk_freq, + __: PhantomData, + } + } +} + +impl Timer { + fn to(self) -> Timer { + Timer { + 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 + } + + 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_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: InterruptSet) -> &mut Self { + self.reg + .count8() + .intenset + .write(|w| unsafe { w.bits(interrupt_set.0) }); + self + } + + pub fn disable_interrupts(&mut self, interrupt_set: InterruptSet) -> &mut Self { + self.reg + .count8() + .intenclr + .write(|w| unsafe { w.bits(interrupt_set.0) }); + self + } + + pub unsafe fn registers(&self) -> &T::Reg { + &self.reg + } +} + +impl Timer { + pub fn disable(self) -> Timer { + self.reg.count8().ctrla.modify(|_, w| w.enable().set_bit()); + while self.reg.count8().syncbusy.read().enable().bit_is_set() {} + self.to() + } + + 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 Timer { + pub fn enable(self) -> Timer { + self.reg.count8().ctrla.modify(|_, w| w.enable().set_bit()); + while self.reg.count8().syncbusy.read().enable().bit_is_set() {} + self.to() + } + + pub fn into_8_bit(self) -> Timer { + self.reg.count8().ctrla.modify(|_, w| w.mode().count8()); + self.to() + } + + pub fn into_16_bit(self) -> Timer { + self.reg.count8().ctrla.modify(|_, w| w.mode().count16()); + self.to() + } + + pub fn set_ondemand(&mut self, value: bool) -> &mut Self { + self.reg + .count8() + .ctrla + .modify(|_, w| w.ondemand().bit(value)); + self + } + + pub fn with_ondemand(mut self, value: bool) -> Self { + self.set_ondemand(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 with_runstdby(mut self, value: bool) -> Self { + self.set_runstdby(value); + self + } + + pub fn with_debug_run(mut self, value: bool) -> Self { + self.set_debug_run(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 + } + + pub fn with_interrupts(mut self, interrupt_set: InterruptSet) -> Self { + 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 + } +} + +impl Timer { + pub fn into_32_bit(self) -> Timer { + self.reg.count8().ctrla.modify(|_, w| w.mode().count32()); + self.to() + } +} + +impl Timer { + pub fn compare(&self, index: TimerCompareRegister) -> TW { + TW::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); + TW::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 + } + + // Clears the interrupts + pub fn interrupt_flags(&self) -> InterruptSet { + let interrupts = self.reg.count8().intflag.read().bits(); + // Clear the interrupts + self.reg + .count8() + .intflag + .write(|w| unsafe { w.bits(interrupts) }); + InterruptSet(interrupts) + } +} + +impl Timer { + // 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, + ) -> (Timer, Timer, I>) { + let _ = primary_clk; + let _ = secondary_clk; + let pclk_freq = common_pclk.freq(); + let _ = common_pclk; + + ( + Timer::new(primary_reg, pclk_freq), + Timer::new(secondary_reg, pclk_freq), + ) + } +} + +pub trait TimersTupleExt { + fn free( + self, + ) -> ( + T::Reg, + ApbClk, + Reg>, + ApbClk>, + Pclk, + ); +} + +impl TimersTupleExt + for (Timer, Timer, 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 Timer +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, + ) -> Timer { + let _ = primary_clk; + let _ = secondary_clk; + let pclk_freq = common_pclk.freq(); + let _ = common_pclk; + Timer::new((primary_reg, secondary_reg).into(), pclk_freq) + } +} + +impl Timer +where + T::Reg: Into<(Reg, Reg)>, +{ + pub 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/interrupt.rs b/hal/src/thumbv7em/timer/v2/interrupt.rs new file mode 100644 index 000000000000..55814bfb5266 --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/interrupt.rs @@ -0,0 +1,99 @@ +use num_traits::FromPrimitive; + +#[repr(u8)] +#[derive(Copy, Clone, PartialEq, Debug, Eq, num_derive::FromPrimitive)] +pub enum Interrupt { + 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 InterruptSet(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 InterruptSet { + pub fn full() -> Self { + Self::from_iter(Self(u8::MAX)) + } +} + +impl From for InterruptSet { + fn from(value: Interrupt) -> Self { + Self(value as _) + } +} + +impl FromIterator for InterruptSet { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().fold(0, |l, r| l | r as u8)) + } +} + +impl Iterator for InterruptSet { + type Item = Interrupt; + + 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 Interrupt::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 = [ + Interrupt::MatchOrCaptureChannel0, + Interrupt::MatchOrCaptureChannel1, + Interrupt::Error, + Interrupt::Overflow, + ]; + let full_interrupt_set_from_u8_max = InterruptSet::full(); + let full_interrupt_set_from_summed_variants = InterruptSet( + Interrupt::MatchOrCaptureChannel0 as u8 + | Interrupt::MatchOrCaptureChannel1 as u8 + | Interrupt::Error as u8 + | Interrupt::Overflow as u8, + ); + let full_interrupt_set = InterruptSet::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 InterruptSet(u8::MAX) { + let is_from = InterruptSet::from(interrupt); + let is_from_iter = InterruptSet::from_iter([interrupt]); + assert_eq!(is_from, is_from_iter); + } + } + #[test] + fn test_empty_interrupt_set() { + let interrupt_set_from_0 = InterruptSet(0); + let interrupt_set_from_empty_iterator = InterruptSet::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 000000000000..ce18a45d7229 --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/mono.rs @@ -0,0 +1,86 @@ +use super::*; + +impl Timer { + pub fn into_monotonic( + self, + ) -> Result, TimerError> { + MonotonicTimer::enable(self) + } +} + +pub struct MonotonicTimer { + inner_timer: Timer, + high_bits: u32, +} + +impl MonotonicTimer { + fn enable(inner: Timer) -> Result { + let inner_timer = inner + .with_frequency(Hertz::Hz(TIMER_HZ))? + .with_direction(TimerDirection::Increment) + .with_interrupts(Interrupt::Overflow.into()) + .enable(); + Ok(Self { + inner_timer, + high_bits: 0, + }) + } + + pub fn disable(self) -> Timer { + self.inner_timer.disable() + } +} + +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.inner_timer.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.inner_timer; + timer.enable_interrupts(Interrupt::MatchOrCaptureChannel0.into()); + timer.set_compare(TimerCompareRegister::Zero, instant.ticks() as u32); + } + } + + fn clear_compare_flag(&mut self) { + let timer = &mut self.inner_timer; + let interrupts = timer.interrupt_flags(); + for interrupt in interrupts { + match interrupt { + Interrupt::Overflow => self.high_bits += 1, + Interrupt::MatchOrCaptureChannel0 => { + timer.disable_interrupts(Interrupt::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.inner_timer.set_count(0); + } +} diff --git a/hal/src/thumbv7em/timer/v2/types.rs b/hal/src/thumbv7em/timer/v2/types.rs new file mode 100644 index 000000000000..7ea034790e7f --- /dev/null +++ b/hal/src/thumbv7em/timer/v2/types.rs @@ -0,0 +1,212 @@ +use core::ops::Deref; + +use crate::clock::v2::apb::ApbId; +use crate::clock::v2::pclk::PclkId; +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 { + pub trait TimerWidth: Copy + Clone + num_traits::AsPrimitive { + fn from_u32(v: u32) -> Self; + } + + impl TimerWidth for u8 { + fn from_u32(v: u32) -> Self { + v as Self + } + } + impl TimerWidth for u16 { + fn from_u32(v: u32) -> Self { + v as Self + } + } + impl TimerWidth for u32 { + fn from_u32(v: u32) -> Self { + v as Self + } + } +} + +#[derive(Copy, Clone)] +pub enum TimerDirection { + Increment, + Decrement, +} + +pub type TimerPrescaler = crate::pac::tc0::count8::ctrla::PRESCALER_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; From 6ad3fbd688ad62ca08916d9fd5f337137189abd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Tue, 13 Jun 2023 18:23:27 +0200 Subject: [PATCH 2/3] Expand Timer into multistage builder + countdown timer impl --- boards/atsame54_xpro/examples/timer_v2.rs | 62 +++- hal/src/prelude.rs | 3 + hal/src/thumbv7em/timer/v2.rs | 418 ++++------------------ hal/src/thumbv7em/timer/v2/builder.rs | 84 +++++ hal/src/thumbv7em/timer/v2/countdown.rs | 170 +++++++++ hal/src/thumbv7em/timer/v2/interrupt.rs | 56 +-- hal/src/thumbv7em/timer/v2/mono.rs | 78 ++-- hal/src/thumbv7em/timer/v2/prelude.rs | 5 + hal/src/thumbv7em/timer/v2/raw.rs | 335 +++++++++++++++++ hal/src/thumbv7em/timer/v2/types.rs | 51 ++- 10 files changed, 838 insertions(+), 424 deletions(-) create mode 100644 hal/src/thumbv7em/timer/v2/builder.rs create mode 100644 hal/src/thumbv7em/timer/v2/countdown.rs create mode 100644 hal/src/thumbv7em/timer/v2/prelude.rs create mode 100644 hal/src/thumbv7em/timer/v2/raw.rs diff --git a/boards/atsame54_xpro/examples/timer_v2.rs b/boards/atsame54_xpro/examples/timer_v2.rs index 44daf6257b5d..290593a5251b 100644 --- a/boards/atsame54_xpro/examples/timer_v2.rs +++ b/boards/atsame54_xpro/examples/timer_v2.rs @@ -22,18 +22,21 @@ 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<32, Tc0Tc1, Gclk1Id>; + type Mono = MonotonicTimer; #[shared] struct Shared {} #[local] struct Local { + countdown: CountdownTimer, button: Button, led: bsp::Led, } @@ -73,23 +76,70 @@ mod app { 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 (tc0_tc1_pclk, gclk1) = clock::pclk::Pclk::enable(tokens.pclks.tc0_tc1, gclk1); - let mono = Timer::paired( + let monotonic = TimerBuilder::paired( ctx.device.TC0, tc0_clk, ctx.device.TC1, tc1_clk, tc0_tc1_pclk, ) - .into_32_bit() .into_monotonic() - .unwrap(); + .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 mut countdown = countdown + .into_8_bit() + .into_countdown() + .unwrap() + .with_saturated_period(1_u32.secs()) + .with_interrupt(); + + unsafe { + countdown + .inner_raw() + .registers() + .count8() + .ctrla + .modify(|_, w| w.prescsync().resync()); + } + + let countdown = countdown.enable(); let led: bsp::Led = bsp::pin_alias!(pins.led).into(); bump_activity_led(); - (Shared {}, Local { button, led }, init::Monotonics(mono)) + ( + 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])] diff --git a/hal/src/prelude.rs b/hal/src/prelude.rs index d4a5cb869188..fe980487a342 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/timer/v2.rs b/hal/src/thumbv7em/timer/v2.rs index a5fe1b8b3e5d..ac952274764b 100644 --- a/hal/src/thumbv7em/timer/v2.rs +++ b/hal/src/thumbv7em/timer/v2.rs @@ -25,9 +25,12 @@ //! Example of how to construct, configure and enable a timer. //! //! ```no_run -//! # use atsamd_hal::clock::v2::pclk::Pclk; -//! # use atsamd_hal::timer::v2::{Timer, TimersTupleExt, TimerPrescaler, TimerDirection}; //! # 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, @@ -50,36 +53,75 @@ //! 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) = Timer::disjoint(tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) /* -> (Timer, Timer) */; -//! let (tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) = (pri, sec).free(); -//! let timer = Timer::paired(tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) /* -> Timer */; -//! let timer = timer -//! .with_prescaler(TimerPrescaler::DIV16) -//! .with_oneshot(true) -//! .with_direction(TimerDirection::Increment) -//! .enable(); +//! 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 (tc0, tc0_clk, tc1, tc1_clk, tc0_tc1_pclk) = timer.free(); +//! 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) = Timer::disjoint(tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) /* -> (Timer, Timer) */; +//! 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 = Timer::paired(tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) /* -> Timer */; -//! let timer = timer.into_32_bit().enable().disable(); -//! let (tc2, tc2_clk, tc3, tc3_clk, tc2_tc3_pclk) = timer.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::marker::PhantomData; +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::MonotonicTimer; +pub use mono::*; + +mod countdown; +pub use countdown::*; mod interrupt; -pub use interrupt::{Interrupt, InterruptSet}; +pub use interrupt::*; mod types; pub use types::*; @@ -92,272 +134,7 @@ use crate::{ time::Hertz, }; -/// General timer abstraction. WiP. -pub struct Timer { - reg: T::Reg, - pclk_freq: Hertz, - __: PhantomData<(I, S, TW)>, -} - -impl Timer { - 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() {} - Self { - reg, - pclk_freq, - __: PhantomData, - } - } -} - -impl Timer { - fn to(self) -> Timer { - Timer { - 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 - } - - 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_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: InterruptSet) -> &mut Self { - self.reg - .count8() - .intenset - .write(|w| unsafe { w.bits(interrupt_set.0) }); - self - } - - pub fn disable_interrupts(&mut self, interrupt_set: InterruptSet) -> &mut Self { - self.reg - .count8() - .intenclr - .write(|w| unsafe { w.bits(interrupt_set.0) }); - self - } - - pub unsafe fn registers(&self) -> &T::Reg { - &self.reg - } -} - -impl Timer { - pub fn disable(self) -> Timer { - self.reg.count8().ctrla.modify(|_, w| w.enable().set_bit()); - while self.reg.count8().syncbusy.read().enable().bit_is_set() {} - self.to() - } - - 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 Timer { - pub fn enable(self) -> Timer { - self.reg.count8().ctrla.modify(|_, w| w.enable().set_bit()); - while self.reg.count8().syncbusy.read().enable().bit_is_set() {} - self.to() - } - - pub fn into_8_bit(self) -> Timer { - self.reg.count8().ctrla.modify(|_, w| w.mode().count8()); - self.to() - } - - pub fn into_16_bit(self) -> Timer { - self.reg.count8().ctrla.modify(|_, w| w.mode().count16()); - self.to() - } - - pub fn set_ondemand(&mut self, value: bool) -> &mut Self { - self.reg - .count8() - .ctrla - .modify(|_, w| w.ondemand().bit(value)); - self - } - - pub fn with_ondemand(mut self, value: bool) -> Self { - self.set_ondemand(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 with_runstdby(mut self, value: bool) -> Self { - self.set_runstdby(value); - self - } - - pub fn with_debug_run(mut self, value: bool) -> Self { - self.set_debug_run(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 - } - - pub fn with_interrupts(mut self, interrupt_set: InterruptSet) -> Self { - 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 - } -} - -impl Timer { - pub fn into_32_bit(self) -> Timer { - self.reg.count8().ctrla.modify(|_, w| w.mode().count32()); - self.to() - } -} - -impl Timer { - pub fn compare(&self, index: TimerCompareRegister) -> TW { - TW::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); - TW::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 - } - - // Clears the interrupts - pub fn interrupt_flags(&self) -> InterruptSet { - let interrupts = self.reg.count8().intflag.read().bits(); - // Clear the interrupts - self.reg - .count8() - .intflag - .write(|w| unsafe { w.bits(interrupts) }); - InterruptSet(interrupts) - } -} - -impl Timer { +impl TimerBuilder { // Create the disjoint pair of timers. They support 8-bit and 16-bit counters pub fn disjoint( primary_reg: T::Reg, @@ -365,54 +142,20 @@ impl Timer { secondary_reg: Reg>, secondary_clk: ApbClk>, common_pclk: Pclk, - ) -> (Timer, Timer, I>) { + ) -> (TimerBuilder, TimerBuilder, I>) { let _ = primary_clk; let _ = secondary_clk; let pclk_freq = common_pclk.freq(); let _ = common_pclk; ( - Timer::new(primary_reg, pclk_freq), - Timer::new(secondary_reg, pclk_freq), - ) - } -} - -pub trait TimersTupleExt { - fn free( - self, - ) -> ( - T::Reg, - ApbClk, - Reg>, - ApbClk>, - Pclk, - ); -} - -impl TimersTupleExt - for (Timer, Timer, 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), + TimerBuilder::new(RawTimer::new(primary_reg, pclk_freq)), + TimerBuilder::new(RawTimer::new(secondary_reg, pclk_freq)), ) } } -impl Timer +impl TimerBuilder where Reg: From<(Reg, Reg>)>, { @@ -423,35 +166,14 @@ where secondary_reg: Reg>, secondary_clk: ApbClk>, common_pclk: Pclk, - ) -> Timer { + ) -> TimerBuilder { let _ = primary_clk; let _ = secondary_clk; let pclk_freq = common_pclk.freq(); let _ = common_pclk; - Timer::new((primary_reg, secondary_reg).into(), pclk_freq) - } -} - -impl Timer -where - T::Reg: Into<(Reg, Reg)>, -{ - pub 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), - ) + 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 000000000000..58d0cda282c9 --- /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 000000000000..e99d60b254f0 --- /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 index 55814bfb5266..6b469c46e819 100644 --- a/hal/src/thumbv7em/timer/v2/interrupt.rs +++ b/hal/src/thumbv7em/timer/v2/interrupt.rs @@ -2,7 +2,7 @@ use num_traits::FromPrimitive; #[repr(u8)] #[derive(Copy, Clone, PartialEq, Debug, Eq, num_derive::FromPrimitive)] -pub enum Interrupt { +pub enum TimerInterrupt { MatchOrCaptureChannel1 = 1 << 5, MatchOrCaptureChannel0 = 1 << 4, Error = 1 << 1, @@ -12,7 +12,7 @@ pub enum Interrupt { bitfield::bitfield! { /// Raw userpage POD struct that exposes bitfields via methods #[derive(Copy, Clone, PartialEq, Eq)] - pub struct InterruptSet(u8); + pub struct TimerInterruptSet(u8); impl Debug; u8; pub overflow, set_overflow: 0; @@ -21,26 +21,30 @@ bitfield::bitfield! { pub match_or_capture_channel_1, set_match_or_capture_channel_1: 5; } -impl InterruptSet { +impl TimerInterruptSet { pub fn full() -> Self { Self::from_iter(Self(u8::MAX)) } + + pub fn empty() -> Self { + Self(0) + } } -impl From for InterruptSet { - fn from(value: Interrupt) -> Self { +impl From for TimerInterruptSet { + fn from(value: TimerInterrupt) -> Self { Self(value as _) } } -impl FromIterator for InterruptSet { - fn from_iter>(iter: T) -> Self { +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 InterruptSet { - type Item = Interrupt; +impl Iterator for TimerInterruptSet { + type Item = TimerInterrupt; fn next(&mut self) -> Option { let raw_interrupt_set = &mut self.0; @@ -49,7 +53,7 @@ impl Iterator for InterruptSet { let raw_interrupt = 1 << raw_interrupt_set.trailing_zeros(); // Clear the bit *raw_interrupt_set &= !raw_interrupt; - match Interrupt::from_u8(raw_interrupt) { + match TimerInterrupt::from_u8(raw_interrupt) { None => continue, Some(i) => return Some(i), } @@ -64,35 +68,35 @@ mod tests { #[test] fn test_full_interrupt_set() { let interrupts = [ - Interrupt::MatchOrCaptureChannel0, - Interrupt::MatchOrCaptureChannel1, - Interrupt::Error, - Interrupt::Overflow, + TimerInterrupt::MatchOrCaptureChannel0, + TimerInterrupt::MatchOrCaptureChannel1, + TimerInterrupt::Error, + TimerInterrupt::Overflow, ]; - let full_interrupt_set_from_u8_max = InterruptSet::full(); - let full_interrupt_set_from_summed_variants = InterruptSet( - Interrupt::MatchOrCaptureChannel0 as u8 - | Interrupt::MatchOrCaptureChannel1 as u8 - | Interrupt::Error as u8 - | Interrupt::Overflow as u8, + 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 = InterruptSet::from_iter(interrupts); + 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 InterruptSet(u8::MAX) { - let is_from = InterruptSet::from(interrupt); - let is_from_iter = InterruptSet::from_iter([interrupt]); + 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 = InterruptSet(0); - let interrupt_set_from_empty_iterator = InterruptSet::from_iter([]); + 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 index ce18a45d7229..1738a9cdff0a 100644 --- a/hal/src/thumbv7em/timer/v2/mono.rs +++ b/hal/src/thumbv7em/timer/v2/mono.rs @@ -1,38 +1,64 @@ use super::*; -impl Timer { - pub fn into_monotonic( +pub trait IntoMonotonic { + fn into_monotonic( + self, + ) -> Result, TimerError>; +} + +impl IntoMonotonic for TimerBuilder { + fn into_monotonic( self, ) -> Result, TimerError> { - MonotonicTimer::enable(self) + 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 { - inner_timer: Timer, +pub struct MonotonicTimer { + raw: RawTimer, high_bits: u32, } impl MonotonicTimer { - fn enable(inner: Timer) -> Result { - let inner_timer = inner - .with_frequency(Hertz::Hz(TIMER_HZ))? - .with_direction(TimerDirection::Increment) - .with_interrupts(Interrupt::Overflow.into()) - .enable(); - Ok(Self { - inner_timer, - high_bits: 0, - }) + 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, + } + } +} - pub fn disable(self) -> Timer { - self.inner_timer.disable() +impl IntoRaw + for MonotonicTimer +{ + fn into_raw(self) -> RawTimer { + self.raw } } impl rtic_monotonic::Monotonic - for MonotonicTimer + for MonotonicTimer { const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false; @@ -41,7 +67,7 @@ impl rtic_monotonic::Monotonic type Duration = fugit::TimerDurationU64; fn now(&mut self) -> Self::Instant { - let ticks = u64::from(self.high_bits) << u32::BITS | u64::from(self.inner_timer.count()); + let ticks = u64::from(self.high_bits) << u32::BITS | u64::from(self.raw.count()); Self::Instant::from_ticks(ticks) } @@ -54,20 +80,20 @@ impl rtic_monotonic::Monotonic .map_or(0, |d| d.ticks()) <= u32::MAX.into() { - let timer = &mut self.inner_timer; - timer.enable_interrupts(Interrupt::MatchOrCaptureChannel0.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.inner_timer; + let timer = &mut self.raw; let interrupts = timer.interrupt_flags(); for interrupt in interrupts { match interrupt { - Interrupt::Overflow => self.high_bits += 1, - Interrupt::MatchOrCaptureChannel0 => { - timer.disable_interrupts(Interrupt::MatchOrCaptureChannel0.into()); + TimerInterrupt::Overflow => self.high_bits += 1, + TimerInterrupt::MatchOrCaptureChannel0 => { + timer.disable_interrupts(TimerInterrupt::MatchOrCaptureChannel0.into()); timer.set_compare(TimerCompareRegister::Zero, 0); } _ => {} @@ -81,6 +107,6 @@ impl rtic_monotonic::Monotonic unsafe fn reset(&mut self) { self.high_bits = 0; - self.inner_timer.set_count(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 000000000000..3bdb925179b7 --- /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 000000000000..11c5d3626859 --- /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 index 7ea034790e7f..8a93b27b32be 100644 --- a/hal/src/thumbv7em/timer/v2/types.rs +++ b/hal/src/thumbv7em/timer/v2/types.rs @@ -1,7 +1,7 @@ use core::ops::Deref; -use crate::clock::v2::apb::ApbId; -use crate::clock::v2::pclk::PclkId; +use crate::clock::v2::apb::{ApbClk, ApbId}; +use crate::clock::v2::pclk::{Pclk, PclkId, PclkSourceId}; use crate::clock::v2::types; use crate::pac; @@ -143,28 +143,29 @@ pub mod state { } pub mod timer_width { - pub trait TimerWidth: Copy + Clone + num_traits::AsPrimitive { - fn from_u32(v: u32) -> Self; + use num_traits::*; + pub trait TimerWidth: Copy + Clone + AsPrimitive + FromPrimitive + Bounded { + fn truncated_from_u32(v: u32) -> Self; } - impl TimerWidth for u8 { - fn from_u32(v: u32) -> Self { - v as Self - } - } - impl TimerWidth for u16 { - fn from_u32(v: u32) -> Self { - v as Self - } - } - impl TimerWidth for u32 { - fn from_u32(v: u32) -> Self { - v as 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)] +#[derive(Copy, Clone, Debug)] pub enum TimerDirection { Increment, Decrement, @@ -172,6 +173,8 @@ pub enum TimerDirection { 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), @@ -210,3 +213,15 @@ pub enum TimerCompareRegister { 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, + ); +} From 4ae5e9958a7d51bb242c5eec29de42301553c034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Thu, 3 Aug 2023 18:35:53 +0200 Subject: [PATCH 3/3] Remove old debug code --- boards/atsame54_xpro/examples/timer_v2.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/boards/atsame54_xpro/examples/timer_v2.rs b/boards/atsame54_xpro/examples/timer_v2.rs index 290593a5251b..9170d077003c 100644 --- a/boards/atsame54_xpro/examples/timer_v2.rs +++ b/boards/atsame54_xpro/examples/timer_v2.rs @@ -104,22 +104,13 @@ mod app { // TODO: Create a PR to fugit to fix extension traits use atsamd_hal::fugit::ExtU32; - let mut countdown = countdown + let countdown = countdown .into_8_bit() .into_countdown() .unwrap() .with_saturated_period(1_u32.secs()) .with_interrupt(); - unsafe { - countdown - .inner_raw() - .registers() - .count8() - .ctrla - .modify(|_, w| w.prescsync().resync()); - } - let countdown = countdown.enable(); let led: bsp::Led = bsp::pin_alias!(pins.led).into();