diff options
Diffstat (limited to 'rtic-monotonics')
| -rw-r--r-- | rtic-monotonics/CHANGELOG.md | 4 | ||||
| -rw-r--r-- | rtic-monotonics/Cargo.toml | 15 | ||||
| -rw-r--r-- | rtic-monotonics/src/imxrt.rs | 227 | ||||
| -rw-r--r-- | rtic-monotonics/src/lib.rs | 18 | ||||
| -rw-r--r-- | rtic-monotonics/src/nrf/rtc.rs | 255 | ||||
| -rw-r--r-- | rtic-monotonics/src/nrf/timer.rs | 285 | ||||
| -rw-r--r-- | rtic-monotonics/src/rp2040.rs | 184 | ||||
| -rw-r--r-- | rtic-monotonics/src/stm32.rs | 309 | ||||
| -rw-r--r-- | rtic-monotonics/src/systick.rs | 236 |
9 files changed, 797 insertions, 736 deletions
diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index ef5840f..f114a18 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -5,10 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). For each category, *Added*, *Changed*, *Fixed* add new entries at the top! -## Unreleased +## Unreleased - v2.0.0 ### Changed +- Rework all timers based on `rtic-time 2.0.0` +- Most timer tick rates are now configurable - Tweak `build.rs` to avoid warnings in Nightly 1.78+ - Removed unused `rust-toolchain.toml` diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 5860653..81df240 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rtic-monotonics" -version = "1.5.0" +version = "2.0.0" edition = "2021" authors = [ @@ -24,13 +24,17 @@ features = [ "imxrt_gpt1", "imxrt_gpt2", "imxrt-ral/imxrt1011", + "stm32h725ag", + "stm32_tim2", + "stm32_tim3", + "stm32_tim4", + "stm32_tim5", + "stm32_tim15", ] rustdoc-flags = ["--cfg", "docsrs"] [dependencies] -rtic-time = { version = "1.1.0", path = "../rtic-time" } -embedded-hal = { version = "1.0" } -embedded-hal-async = { version = "1.0", optional = true } +rtic-time = { version = "2.0.0", path = "../rtic-time" } fugit = { version = "0.3.6" } atomic-polyfill = "1" cfg-if = "1.0.0" @@ -69,8 +73,6 @@ defmt = ["fugit/defmt"] # Systick on Cortex-M, default 1 kHz cortex-m-systick = ["dep:cortex-m"] -systick-100hz = [] -systick-10khz = [] # Use 64-bit wide backing storage for the Instant systick-64bit = [] @@ -99,7 +101,6 @@ stm32_tim2 = [] stm32_tim3 = [] stm32_tim4 = [] stm32_tim5 = [] -stm32_tim12 = [] stm32_tim15 = [] stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"] diff --git a/rtic-monotonics/src/imxrt.rs b/rtic-monotonics/src/imxrt.rs index 2299bea..6d07be6 100644 --- a/rtic-monotonics/src/imxrt.rs +++ b/rtic-monotonics/src/imxrt.rs @@ -1,112 +1,158 @@ -//! [`Monotonic`] implementations for i.MX RT's GPT peripherals. +//! [`Monotonic`](rtic_time::Monotonic) implementations for i.MX RT's GPT peripherals. //! //! # Example //! //! ``` -//! use rtic_monotonics::imxrt::*; -//! use rtic_monotonics::imxrt::Gpt1 as Mono; +//! use rtic_monotonics::imxrt::prelude::*; +//! imxrt_gpt1_monotonic!(Mono, 1_000_000); //! //! fn init() { -//! // Obtain ownership of the timer register block +//! // Obtain ownership of the timer register block. //! let gpt1 = unsafe { imxrt_ral::gpt::GPT1::instance() }; //! -//! // Configure the timer clock source and determine its tick rate -//! let timer_tickrate_hz = 1_000_000; -//! -//! // Generate timer token to ensure correct timer interrupt handler is used -//! let token = rtic_monotonics::create_imxrt_gpt1_token!(); +//! // Configure the timer tick rate as specified earlier +//! todo!("Configure the gpt1 peripheral to a tick rate of 1_000_000"); //! //! // Start the monotonic -//! Mono::start(timer_tickrate_hz, gpt1, token); +//! Mono::start(gpt1); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! let timestamp = Mono::now().ticks(); +//! let timestamp = Mono::now(); //! Mono::delay(100.millis()).await; //! } //! } //! ``` -use crate::{Monotonic, TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; - -use imxrt_ral as ral; - -const TIMER_HZ: u32 = 1_000_000; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, +}; + +pub use imxrt_ral as ral; + +/// Common definitions and traits for using the i.MX RT monotonics +pub mod prelude { + #[cfg(feature = "imxrt_gpt1")] + pub use crate::imxrt_gpt1_monotonic; + #[cfg(feature = "imxrt_gpt2")] + pub use crate::imxrt_gpt2_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} #[doc(hidden)] #[macro_export] macro_rules! __internal_create_imxrt_timer_interrupt { - ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + ($mono_backend:ident, $timer:ident) => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn $timer() { - $crate::imxrt::$mono_timer::__tq().on_monotonic_interrupt(); + use $crate::TimerQueueBackend; + $crate::imxrt::$mono_backend::timer_queue().on_monotonic_interrupt(); } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_imxrt_timer_struct { + ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { + /// A `Monotonic` based on the GPT peripheral. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// This method must be called only once. + pub fn start(gpt: $crate::imxrt::ral::gpt::$timer) { + $crate::__internal_create_imxrt_timer_interrupt!($mono_backend, $timer); - pub struct $timer_token; + $crate::imxrt::$mono_backend::_start(gpt); + } + } - unsafe impl $crate::InterruptToken<$crate::imxrt::$mono_timer> for $timer_token {} + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::imxrt::$mono_backend; + type Instant = $crate::fugit::Instant< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + } - $timer_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register the GPT1 interrupt for the monotonic. +/// Create a GPT1 based monotonic and register the GPT1 interrupt for it. +/// +/// See [`crate::imxrt`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility +/// to configure the peripheral to the given frequency before starting the +/// monotonic. #[cfg(feature = "imxrt_gpt1")] #[macro_export] -macro_rules! create_imxrt_gpt1_token { - () => {{ - $crate::__internal_create_imxrt_timer_interrupt!(Gpt1, GPT1, Gpt1Token) - }}; +macro_rules! imxrt_gpt1_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_imxrt_timer_struct!($name, Gpt1Backend, GPT1, $tick_rate_hz); + }; } -/// Register the GPT2 interrupt for the monotonic. +/// Create a GPT2 based monotonic and register the GPT2 interrupt for it. +/// +/// See [`crate::imxrt`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility +/// to configure the peripheral to the given frequency before starting the +/// monotonic. #[cfg(feature = "imxrt_gpt2")] #[macro_export] -macro_rules! create_imxrt_gpt2_token { - () => {{ - $crate::__internal_create_imxrt_timer_interrupt!(Gpt2, GPT2, Gpt2Token) - }}; +macro_rules! imxrt_gpt2_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_imxrt_timer_struct!($name, Gpt2Backend, GPT2, $tick_rate_hz); + }; } macro_rules! make_timer { - ($mono_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Timer implementing [`Monotonic`] which runs at 1 MHz. + ($mono_name:ident, $backend_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// GPT based [`TimerQueueBackend`]. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; use ral::gpt::$timer; /// Number of 2^31 periods elapsed since boot. static $period: AtomicU32 = AtomicU32::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); - impl $mono_name { - /// Starts the monotonic timer. + impl $backend_name { + /// Starts the timer. /// - /// - `tick_freq_hz`: The tick frequency of the given timer. - /// - `gpt`: The GPT timer register block instance. - /// - `_interrupt_token`: Required for correct timer interrupt handling. + /// **Do not use this function directly.** /// - /// This method must be called only once. - pub fn start(tick_freq_hz: u32, gpt: $timer, _interrupt_token: impl crate::InterruptToken<Self>) { - // Find a prescaler that creates our desired tick frequency - let previous_prescaler = ral::read_reg!(ral::gpt, gpt, PR, PRESCALER) + 1; - let previous_clock_freq = tick_freq_hz * previous_prescaler; - assert!((previous_clock_freq % TIMER_HZ) == 0, - "Unable to find a fitting prescaler value!\n Input: {}/{}\n Desired: {}", - previous_clock_freq, previous_prescaler, TIMER_HZ); - let prescaler = previous_clock_freq / TIMER_HZ; - assert!(prescaler > 0); - assert!(prescaler <= 4096); + /// Use the prelude macros instead. + pub fn _start(gpt: $timer) { // Disable the timer. ral::modify_reg!(ral::gpt, gpt, CR, EN: 0); @@ -122,11 +168,6 @@ macro_rules! make_timer { // Reset period $period.store(0, Ordering::SeqCst); - // Prescaler - ral::modify_reg!(ral::gpt, gpt, PR, - PRESCALER: (prescaler - 1), // Scale to our desired clock rate - ); - // Enable interrupts ral::write_reg!(ral::gpt, gpt, IR, ROVIE: 1, // Rollover interrupt @@ -150,7 +191,6 @@ macro_rules! make_timer { ENMOD: 0, // Keep state when disabled ); - // SAFETY: We take full ownership of the peripheral and interrupt vector, // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. @@ -159,65 +199,21 @@ macro_rules! make_timer { cortex_m::peripheral::NVIC::unmask(ral::Interrupt::$timer); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: <Self as Monotonic>::Duration) { - $tq.delay(duration).await; - } - - /// Timeout at a specific time. - pub async fn timeout_at<F: core::future::Future>( - instant: <Self as rtic_time::Monotonic>::Instant, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after<F: core::future::Future>( - duration: <Self as Monotonic>::Duration, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_after(duration, future).await - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: <Self as Monotonic>::Instant) { - $tq.delay_until(instant).await; - } } - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); - - impl Monotonic for $mono_name { - type Instant = fugit::TimerInstantU64<TIMER_HZ>; - type Duration = fugit::TimerDurationU64<TIMER_HZ>; + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { let gpt = unsafe{ $timer::instance() }; - Self::Instant::from_ticks(calculate_now( + calculate_now( || $period.load(Ordering::Relaxed), || ral::read_reg!(ral::gpt, gpt, CNT) - )) + ) } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let gpt = unsafe{ $timer::instance() }; // Set the timer regardless of whether it is multiple periods in the future, @@ -225,8 +221,7 @@ macro_rules! make_timer { // The worst thing that can happen is a spurious wakeup, and with a timer // period of half an hour, this is hardly a problem. - let ticks = instant.duration_since_epoch().ticks(); - let ticks_wrapped = ticks as u32; + let ticks_wrapped = instant as u32; ral::write_reg!(ral::gpt, gpt, OCR[1], ticks_wrapped); } @@ -257,12 +252,16 @@ macro_rules! make_timer { assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!"); } } + + fn timer_queue() -> &'static TimerQueue<Self> { + &$tq + } } }; } #[cfg(feature = "imxrt_gpt1")] -make_timer!(Gpt1, GPT1, GPT1_HALFPERIODS, GPT1_TQ); +make_timer!(Gpt1, Gpt1Backend, GPT1, GPT1_HALFPERIODS, GPT1_TQ); #[cfg(feature = "imxrt_gpt2")] -make_timer!(Gpt2, GPT2, GPT2_HALFPERIODS, GPT2_TQ); +make_timer!(Gpt2, Gpt2Backend, GPT2, GPT2_HALFPERIODS, GPT2_TQ); diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 6dc703e..65180b1 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -21,14 +21,18 @@ //! `Available on crate features X only` tag are available on any `nrf52*` feature. //! // To build these docs correctly: -// RUSTFLAGS="--cfg docsrs" cargo doc --featuers cortex-m-systick,rp2040,nrf52840 +// RUSTFLAGS="--cfg docsrs" cargo +nightly doc --features thumbv7-backend,cortex-m-systick,rp2040,nrf52840,imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1011,stm32h725ag,stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15 #![no_std] #![deny(missing_docs)] #![allow(incomplete_features)] #![cfg_attr(docsrs, feature(doc_cfg))] -pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; +pub use fugit; +pub use rtic_time::{ + self, monotonic::TimerQueueBasedMonotonic, timer_queue::TimerQueueBackend, Monotonic, + TimeoutError, +}; #[cfg(feature = "cortex-m-systick")] pub mod systick; @@ -92,13 +96,3 @@ pub(crate) unsafe fn set_monotonic_prio( nvic.set_priority(interrupt, hw_prio); } - -/// This marker is implemented on an interrupt token to enforce that the right tokens -/// are given to the correct monotonic implementation. -/// -/// This trait is implemented by this crate and not intended for user implementation. -/// -/// # Safety -/// -/// This is only safely implemented by this crate. -pub unsafe trait InterruptToken<Periperhal> {} diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs index d425b11..39b26c4 100644 --- a/rtic-monotonics/src/nrf/rtc.rs +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -1,96 +1,152 @@ -//! [`Monotonic`] implementation for the nRF Real Time Clocks (RTC). +//! [`Monotonic`](rtic_time::Monotonic) implementation for the nRF Real Time Clocks (RTC). //! //! # Example //! //! ``` -//! use rtic_monotonics::nrf::rtc::*; +//! use rtic_monotonics::nrf::rtc::prelude::*; +//! nrf_rtc0_monotonic!(Mono); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let rtc = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); -//! //! // Start the monotonic -//! Rtc0::start(rtc, token); +//! Mono::start(rtc); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! Rtc0::delay(100.millis()).await; +//! let timestamp = Mono::now(); +//! Mono::delay(100.millis()).await; //! } //! } //! ``` +/// Common definitions and traits for using the nRF RTC monotonics +pub mod prelude { + pub use crate::nrf_rtc0_monotonic; + pub use crate::nrf_rtc1_monotonic; + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] + pub use crate::nrf_rtc2_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} + #[cfg(feature = "nrf52810")] -use nrf52810_pac::{self as pac, Interrupt, RTC0, RTC1}; +#[doc(hidden)] +pub use nrf52810_pac::{self as pac, RTC0, RTC1}; #[cfg(feature = "nrf52811")] -use nrf52811_pac::{self as pac, Interrupt, RTC0, RTC1}; +#[doc(hidden)] +pub use nrf52811_pac::{self as pac, RTC0, RTC1}; #[cfg(feature = "nrf52832")] -use nrf52832_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[doc(hidden)] +pub use nrf52832_pac::{self as pac, RTC0, RTC1, RTC2}; #[cfg(feature = "nrf52833")] -use nrf52833_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[doc(hidden)] +pub use nrf52833_pac::{self as pac, RTC0, RTC1, RTC2}; #[cfg(feature = "nrf52840")] -use nrf52840_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[doc(hidden)] +pub use nrf52840_pac::{self as pac, RTC0, RTC1, RTC2}; #[cfg(feature = "nrf5340-app")] -use nrf5340_app_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[doc(hidden)] +pub use nrf5340_app_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1}; #[cfg(feature = "nrf5340-net")] -use nrf5340_net_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[doc(hidden)] +pub use nrf5340_net_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1}; #[cfg(feature = "nrf9160")] -use nrf9160_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[doc(hidden)] +pub use nrf9160_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1}; -use crate::{Monotonic, TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; -use core::future::Future; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, +}; #[doc(hidden)] #[macro_export] macro_rules! __internal_create_nrf_rtc_interrupt { - ($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{ + ($mono_backend:ident, $rtc:ident) => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn $rtc() { - $crate::nrf::rtc::$mono_timer::__tq().on_monotonic_interrupt(); + use $crate::TimerQueueBackend; + $crate::nrf::rtc::$mono_backend::timer_queue().on_monotonic_interrupt(); } + }; +} - pub struct $rtc_token; +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_nrf_rtc_struct { + ($name:ident, $mono_backend:ident, $timer:ident) => { + /// A `Monotonic` based on the nRF RTC peripheral. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// This method must be called only once. + pub fn start(rtc: $crate::nrf::rtc::$timer) { + $crate::__internal_create_nrf_rtc_interrupt!($mono_backend, $timer); + + $crate::nrf::rtc::$mono_backend::_start(rtc); + } + } - unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {} + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::nrf::rtc::$mono_backend; + type Instant = $crate::fugit::Instant< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + 32_768, + >; + type Duration = $crate::fugit::Duration< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + 32_768, + >; + } - $rtc_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register the Rtc0 interrupt for the monotonic. +/// Create an RTC0 based monotonic and register the RTC0 interrupt for it. +/// +/// See [`crate::nrf::rtc`] for more details. #[macro_export] -macro_rules! create_nrf_rtc0_monotonic_token { - () => {{ - $crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token) - }}; +macro_rules! nrf_rtc0_monotonic { + ($name:ident) => { + $crate::__internal_create_nrf_rtc_struct!($name, Rtc0Backend, RTC0); + }; } -/// Register the Rtc1 interrupt for the monotonic. +/// Create an RTC1 based monotonic and register the RTC1 interrupt for it. +/// +/// See [`crate::nrf::rtc`] for more details. #[macro_export] -macro_rules! create_nrf_rtc1_monotonic_token { - () => {{ - $crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token) - }}; +macro_rules! nrf_rtc1_monotonic { + ($name:ident) => { + $crate::__internal_create_nrf_rtc_struct!($name, Rtc1Backend, RTC1); + }; } -/// Register the Rtc2 interrupt for the monotonic. +/// Create an RTC2 based monotonic and register the RTC2 interrupt for it. +/// +/// See [`crate::nrf::rtc`] for more details. #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) )] #[macro_export] -macro_rules! create_nrf_rtc2_monotonic_token { - () => {{ - $crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token) - }}; +macro_rules! nrf_rtc2_monotonic { + ($name:ident) => { + $crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2); + }; } struct TimerValueU24(u32); @@ -104,19 +160,23 @@ impl From<TimerValueU24> for u64 { } macro_rules! make_rtc { - ($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Monotonic timer queue implementation. + ($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// RTC based [`TimerQueueBackend`]. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; static $overflow: AtomicU32 = AtomicU32::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); - - impl $mono_name { - /// Start the timer monotonic. - pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken<Self>) { + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); + + impl $backend_name { + /// Starts the timer. + /// + /// **Do not use this function directly.** + /// + /// Use the prelude macros instead. + pub fn _start(rtc: $rtc) { unsafe { rtc.prescaler.write(|w| w.bits(0)) }; // Disable interrupts, as preparation @@ -166,67 +226,21 @@ macro_rules! make_rtc { // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. unsafe { - crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc); - pac::NVIC::unmask(Interrupt::$rtc); + crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc); + pac::NVIC::unmask(pac::Interrupt::$rtc); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Timeout at a specific time. - #[inline] - pub async fn timeout_at<F: Future>( - instant: <Self as Monotonic>::Instant, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after<F: Future>( - duration: <Self as Monotonic>::Duration, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: <Self as Monotonic>::Duration) { - $tq.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: <Self as Monotonic>::Instant) { - $tq.delay_until(instant).await; - } } + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); - - impl Monotonic for $mono_name { - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - type Instant = fugit::TimerInstantU64<32_768>; - type Duration = fugit::TimerDurationU64<32_768>; - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { let rtc = unsafe { &*$rtc::PTR }; - Self::Instant::from_ticks(calculate_now( + calculate_now( || $overflow.load(Ordering::Relaxed), || TimerValueU24(rtc.counter.read().bits()) - )) + ) } fn on_interrupt() { @@ -243,28 +257,35 @@ macro_rules! make_rtc { } } - fn enable_timer() {} - - fn disable_timer() {} - - fn set_compare(mut instant: Self::Instant) { + fn set_compare(mut instant: Self::Ticks) { let rtc = unsafe { &*$rtc::PTR }; + const MAX: u64 = 0xff_ffff; + // Disable interrupts because this section is timing critical. // We rely on the fact that this entire section runs within one // RTC clock tick. (which it will do easily if it doesn't get // interrupted) critical_section::with(|_|{ let now = Self::now(); - if let Some(diff) = instant.checked_duration_since(now) { + // wrapping_sub deals with the u64 overflow corner case + let diff = instant.wrapping_sub(now); + let val = if diff <= MAX { + // Now we know `instant` whill happen within one `MAX` time duration. + // Errata: Timer interrupts don't fire if they are scheduled less than // two ticks in the future. Make it three, because the timer could // tick right now. - if diff.ticks() < 3 { - instant = Self::Instant::from_ticks(now.ticks().wrapping_add(3)); + if diff < 3 { + instant = now.wrapping_add(3); } - unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) }; - } + + (instant & MAX) as u32 + } else { + 0 + }; + + unsafe { rtc.cc[0].write(|w| w.bits(val)) }; }); } @@ -274,13 +295,17 @@ macro_rules! make_rtc { } fn pend_interrupt() { - pac::NVIC::pend(Interrupt::$rtc); + pac::NVIC::pend(pac::Interrupt::$rtc); + } + + fn timer_queue() -> &'static TimerQueue<Self> { + &$tq } } }; } -make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ); -make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ); +make_rtc!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ); +make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] -make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); +make_rtc!(Rtc2Backend, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs index 7b760e4..60ee7dc 100644 --- a/rtic-monotonics/src/nrf/timer.rs +++ b/rtic-monotonics/src/nrf/timer.rs @@ -1,4 +1,4 @@ -//! [`Monotonic`] impl for the 32-bit timers of the nRF series. +//! [`Monotonic`](rtic_time::Monotonic) implementation for the 32-bit timers of the nRF series. //! //! Not all timers are available on all parts. Ensure that only the available //! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`. @@ -6,139 +6,217 @@ //! # Example //! //! ``` -//! use rtic_monotonics::nrf::timer::*; +//! use rtic_monotonics::nrf::timer::prelude::*; +//! nrf_timer0_monotonic!(Mono); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let timer = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); -//! //! // Start the monotonic -//! Timer0::start(timer, token); +//! Mono::start(timer); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! Timer0::delay(100.millis()).await; +//! let timestamp = Mono::now(); +//! Mono::delay(100.millis()).await; //! } //! } //! ``` -use crate::{Monotonic, TimeoutError, TimerQueue}; -use atomic_polyfill::{AtomicU32, Ordering}; -use core::future::Future; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +/// Common definitions and traits for using the nRF Timer monotonics +pub mod prelude { + pub use crate::nrf_timer0_monotonic; + pub use crate::nrf_timer1_monotonic; + pub use crate::nrf_timer2_monotonic; + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] + pub use crate::nrf_timer3_monotonic; + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] + pub use crate::nrf_timer4_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} #[cfg(feature = "nrf52810")] -use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[doc(hidden)] +pub use nrf52810_pac::{self as pac, TIMER0, TIMER1, TIMER2}; #[cfg(feature = "nrf52811")] -use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[doc(hidden)] +pub use nrf52811_pac::{self as pac, TIMER0, TIMER1, TIMER2}; #[cfg(feature = "nrf52832")] -use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[doc(hidden)] +pub use nrf52832_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[cfg(feature = "nrf52833")] -use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[doc(hidden)] +pub use nrf52833_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[cfg(feature = "nrf52840")] -use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[doc(hidden)] +pub use nrf52840_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[cfg(feature = "nrf5340-app")] -use nrf5340_app_pac::{ - self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +#[doc(hidden)] +pub use nrf5340_app_pac::{ + self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, }; #[cfg(feature = "nrf5340-net")] -use nrf5340_net_pac::{ - self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +#[doc(hidden)] +pub use nrf5340_net_pac::{ + self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, }; #[cfg(feature = "nrf9160")] -use nrf9160_pac::{ - self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +#[doc(hidden)] +pub use nrf9160_pac::{self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2}; + +use atomic_polyfill::{AtomicU32, Ordering}; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, }; #[doc(hidden)] #[macro_export] macro_rules! __internal_create_nrf_timer_interrupt { - ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + ($mono_backend:ident, $timer:ident) => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn $timer() { - $crate::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt(); + use $crate::TimerQueueBackend; + $crate::nrf::timer::$mono_backend::timer_queue().on_monotonic_interrupt(); } + }; +} - pub struct $timer_token; +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_nrf_timer_struct { + ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { + /// A `Monotonic` based on the nRF Timer peripheral. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// This method must be called only once. + pub fn start(timer: $crate::nrf::timer::$timer) { + $crate::__internal_create_nrf_timer_interrupt!($mono_backend, $timer); + + const PRESCALER: u8 = match $tick_rate_hz { + 16_000_000 => 0, + 8_000_000 => 1, + 4_000_000 => 2, + 2_000_000 => 3, + 1_000_000 => 4, + 500_000 => 5, + 250_000 => 6, + 125_000 => 7, + 62_500 => 8, + 31_250 => 9, + _ => panic!("Timer cannot run at desired tick rate!"), + }; + + $crate::nrf::timer::$mono_backend::_start(timer, PRESCALER); + } + } - unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {} + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::nrf::timer::$mono_backend; + type Instant = $crate::fugit::Instant< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + } - $timer_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register the Timer0 interrupt for the monotonic. +/// Create an Timer0 based monotonic and register the TIMER0 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[macro_export] -macro_rules! create_nrf_timer0_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token) - }}; +macro_rules! nrf_timer0_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer0Backend, TIMER0, $tick_rate_hz); + }; } -/// Register the Timer1 interrupt for the monotonic. +/// Create an Timer1 based monotonic and register the TIMER1 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[macro_export] -macro_rules! create_nrf_timer1_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token) - }}; +macro_rules! nrf_timer1_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer1Backend, TIMER1, $tick_rate_hz); + }; } -/// Register the Timer2 interrupt for the monotonic. +/// Create an Timer2 based monotonic and register the TIMER2 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[macro_export] -macro_rules! create_nrf_timer2_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token) - }}; +macro_rules! nrf_timer2_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer2Backend, TIMER2, $tick_rate_hz); + }; } -/// Register the Timer3 interrupt for the monotonic. +/// Create an Timer3 based monotonic and register the TIMER3 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[cfg_attr( docsrs, doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) )] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[macro_export] -macro_rules! create_nrf_timer3_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token) - }}; +macro_rules! nrf_timer3_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer3Backend, TIMER3, $tick_rate_hz); + }; } -/// Register the Timer4 interrupt for the monotonic. +/// Create an Timer4 based monotonic and register the TIMER4 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[cfg_attr( docsrs, doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) )] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[macro_export] -macro_rules! create_nrf_timer4_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token) - }}; +macro_rules! nrf_timer4_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer4Backend, TIMER4, $tick_rate_hz); + }; } macro_rules! make_timer { - ($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Monotonic timer queue implementation. + ($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// Timer peripheral based [`TimerQueueBackend`]. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; static $overflow: AtomicU32 = AtomicU32::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); - - impl $mono_name { - /// Start the timer monotonic. - pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken<Self>) { - // 1 MHz - timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); + + impl $backend_name { + /// Starts the timer. + /// + /// **Do not use this function directly.** + /// + /// Use the prelude macros instead. + pub fn _start(timer: $timer, prescaler: u8) { + timer.prescaler.write(|w| unsafe { w.prescaler().bits(prescaler) }); timer.bitmode.write(|w| w.bitmode()._32bit()); // Disable interrupts, as preparation @@ -184,70 +262,25 @@ macro_rules! make_timer { // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. unsafe { - crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer); - pac::NVIC::unmask(Interrupt::$timer); + crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$timer); + pac::NVIC::unmask(pac::Interrupt::$timer); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Timeout at a specific time. - #[inline] - pub async fn timeout_at<F: Future>( - instant: <Self as Monotonic>::Instant, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after<F: Future>( - duration: <Self as Monotonic>::Duration, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: <Self as Monotonic>::Duration) { - $tq.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: <Self as Monotonic>::Instant) { - $tq.delay_until(instant).await; - } } - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - impl Monotonic for $mono_name { - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - type Instant = fugit::TimerInstantU64<1_000_000>; - type Duration = fugit::TimerDurationU64<1_000_000>; - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { let timer = unsafe { &*$timer::PTR }; - Self::Instant::from_ticks(calculate_now( + calculate_now( || $overflow.load(Ordering::Relaxed), || { timer.tasks_capture[3].write(|w| unsafe { w.bits(1) }); timer.cc[3].read().bits() } - )) + ) } fn on_interrupt() { @@ -268,9 +301,9 @@ macro_rules! make_timer { } } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let timer = unsafe { &*$timer::PTR }; - timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) }); + timer.cc[0].write(|w| unsafe { w.cc().bits(instant as u32) }); } fn clear_compare_flag() { @@ -279,16 +312,20 @@ macro_rules! make_timer { } fn pend_interrupt() { - pac::NVIC::pend(Interrupt::$timer); + pac::NVIC::pend(pac::Interrupt::$timer); + } + + fn timer_queue() -> &'static TimerQueue<$backend_name> { + &$tq } } }; } -make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); -make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); -make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); +make_timer!(Timer0Backend, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); +make_timer!(Timer1Backend, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); +make_timer!(Timer2Backend, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] -make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); +make_timer!(Timer3Backend, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] -make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); +make_timer!(Timer4Backend, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 998b532..d4f590f 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -1,46 +1,56 @@ -//! [`Monotonic`] implementation for RP2040's Timer peripheral. +//! [`Monotonic`](rtic_time::Monotonic) implementation for RP2040's Timer peripheral. +//! +//! Always runs at a fixed rate of 1 MHz. //! //! # Example //! //! ``` -//! use rtic_monotonics::rp2040::*; +//! use rtic_monotonics::rp2040::prelude::*; +//! +//! rp2040_timer_monotonic!(Mono); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let timer = unsafe { core::mem::transmute(()) }; //! # let mut resets = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_rp2040_monotonic_token!(); -//! +//! # //! // Start the monotonic -//! Timer::start(timer, &mut resets, token); +//! Mono::start(timer, &mut resets); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! Timer::delay(100.millis()).await; +//! let timestamp = Mono::now(); +//! Mono::delay(100.millis()).await; //! } //! } //! ``` -use super::Monotonic; +/// Common definitions and traits for using the RP2040 timer monotonic +pub mod prelude { + pub use crate::rp2040_timer_monotonic; -pub use super::{TimeoutError, TimerQueue}; -use core::future::Future; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rp2040_pac::{timer, Interrupt, NVIC, RESETS, TIMER}; + pub use crate::Monotonic; -/// Timer implementing [`Monotonic`] which runs at 1 MHz. -pub struct Timer; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} -impl Timer { - /// Start a `Monotonic` based on RP2040's Timer. - pub fn start( - timer: TIMER, - resets: &RESETS, - _interrupt_token: impl crate::InterruptToken<Self>, - ) { +use crate::TimerQueueBackend; +use rp2040_pac::{timer, Interrupt, NVIC}; +pub use rp2040_pac::{RESETS, TIMER}; +use rtic_time::timer_queue::TimerQueue; + +/// Timer implementing [`TimerQueueBackend`]. +pub struct TimerBackend; + +impl TimerBackend { + /// Starts the monotonic timer. + /// + /// **Do not use this function directly.** + /// + /// Use the prelude macros instead. + pub fn _start(timer: TIMER, resets: &RESETS) { resets.reset.modify(|_, w| w.timer().clear_bit()); while resets.reset_done.read().timer().bit_is_clear() {} timer.inte.modify(|_, w| w.alarm_0().bit(true)); @@ -58,55 +68,12 @@ impl Timer { } } -static TIMER_QUEUE: TimerQueue<Timer> = TimerQueue::new(); - -// Forward timerqueue interface -impl Timer { - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<Timer> { - &TIMER_QUEUE - } - - /// Timeout at a specific time. - #[inline] - pub async fn timeout_at<F: Future>( - instant: <Self as Monotonic>::Instant, - future: F, - ) -> Result<F::Output, TimeoutError> { - TIMER_QUEUE.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after<F: Future>( - duration: <Self as Monotonic>::Duration, - future: F, - ) -> Result<F::Output, TimeoutError> { - TIMER_QUEUE.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: <Self as Monotonic>::Duration) { - TIMER_QUEUE.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: <Self as Monotonic>::Instant) { - TIMER_QUEUE.delay_until(instant).await; - } -} - -impl Monotonic for Timer { - type Instant = fugit::TimerInstantU64<1_000_000>; - type Duration = fugit::TimerDurationU64<1_000_000>; +static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new(); - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); +impl TimerQueueBackend for TimerBackend { + type Ticks = u64; - fn now() -> Self::Instant { + fn now() -> Self::Ticks { let timer = Self::timer(); let mut hi0 = timer.timerawh.read().bits(); @@ -114,22 +81,24 @@ impl Monotonic for Timer { let low = timer.timerawl.read().bits(); let hi1 = timer.timerawh.read().bits(); if hi0 == hi1 { - break Self::Instant::from_ticks((u64::from(hi0) << 32) | u64::from(low)); + break ((u64::from(hi0) << 32) | u64::from(low)); } hi0 = hi1; } } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let now = Self::now(); - let max = u32::MAX as u64; + const MAX: u64 = u32::MAX as u64; // Since the timer may or may not overflow based on the requested compare val, we check // how many ticks are left. - let val = match instant.checked_duration_since(now) { - Some(x) if x.ticks() <= max => instant.duration_since_epoch().ticks() & max, // Will not overflow - _ => 0, // Will overflow or in the past, set the same value as after overflow to not get extra interrupts + // `wrapping_sup` takes care of the u64 integer overflow special case. + let val = if instant.wrapping_sub(now) <= MAX { + instant & MAX + } else { + 0 }; Self::timer() @@ -145,32 +114,55 @@ impl Monotonic for Timer { rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0); } - fn on_interrupt() {} - - fn enable_timer() {} - - fn disable_timer() {} + fn timer_queue() -> &'static TimerQueue<Self> { + &TIMER_QUEUE + } } -rtic_time::embedded_hal_delay_impl_fugit64!(Timer); - -#[cfg(feature = "embedded-hal-async")] -rtic_time::embedded_hal_async_delay_impl_fugit64!(Timer); - -/// Register the Timer interrupt for the monotonic. +/// Create an RP2040 timer based monotonic and register the necessary interrupt for it. +/// +/// See [`crate::rp2040`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. #[macro_export] -macro_rules! create_rp2040_monotonic_token { - () => {{ - #[no_mangle] - #[allow(non_snake_case)] - unsafe extern "C" fn TIMER_IRQ_0() { - $crate::rp2040::Timer::__tq().on_monotonic_interrupt(); +macro_rules! rp2040_timer_monotonic { + ($name:ident) => { + /// A `Monotonic` based on the RP2040 Timer peripheral. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// This method must be called only once. + pub fn start(timer: $crate::rp2040::TIMER, resets: &$crate::rp2040::RESETS) { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn TIMER_IRQ_0() { + use $crate::TimerQueueBackend; + $crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt(); + } + + $crate::rp2040::TimerBackend::_start(timer, resets); + } } - pub struct Rp2040Token; - - unsafe impl $crate::InterruptToken<$crate::rp2040::Timer> for Rp2040Token {} + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::rp2040::TimerBackend; + type Instant = $crate::fugit::Instant< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + 1_000_000, + >; + type Duration = $crate::fugit::Duration< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + 1_000_000, + >; + } - Rp2040Token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs index 68f95a2..92800c7 100644 --- a/rtic-monotonics/src/stm32.rs +++ b/rtic-monotonics/src/stm32.rs @@ -1,4 +1,4 @@ -//! [`Monotonic`] impl for the STM32. +//! [`Monotonic`](rtic_time::Monotonic) implementations for STM32 chips. //! //! Not all timers are available on all parts. Ensure that only available //! timers are exposed by having the correct `stm32*` feature enabled for `rtic-monotonics`. @@ -6,38 +6,56 @@ //! # Example //! //! ``` -//! use rtic_monotonics::stm32::*; -//! use rtic_monotonics::stm32::Tim2 as Mono; -//! use rtic_monotonics::Monotonic; -//! use embassy_stm32::peripherals::TIM2; -//! use embassy_stm32::rcc::low_level::RccPeripheral; +//! use rtic_monotonics::stm32::prelude::*; //! -//! fn init() { -//! // Generate timer token to ensure correct timer interrupt handler is used. -//! let token = rtic_monotonics::create_stm32_tim2_monotonic_token!(); +//! // Define the monotonic and set it to 1MHz tick rate +//! stm32_tim2_monotonic!(Mono, 1_000_000); //! +//! fn init() { //! // If using `embassy-stm32` HAL, timer clock can be read out like this: -//! let timer_clock_hz = TIM2::frequency(); +//! let timer_clock_hz = embassy_stm32::peripherals::TIM2::frequency(); //! // Or define it manually if you are using other HAL or know correct frequency: //! let timer_clock_hz = 64_000_000; //! //! // Start the monotonic -//! Mono::start(timer_clock_hz, token); +//! Mono::start(timer_clock_hz); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! let timestamp = Mono::now().ticks(); +//! let timestamp = Mono::now(); //! Mono::delay(100.millis()).await; //! } //! } //! ``` -use crate::{Monotonic, TimeoutError, TimerQueue}; +/// Common definitions and traits for using the STM32 monotonics +pub mod prelude { + #[cfg(feature = "stm32_tim2")] + pub use crate::stm32_tim2_monotonic; + + #[cfg(feature = "stm32_tim3")] + pub use crate::stm32_tim3_monotonic; + + #[cfg(feature = "stm32_tim4")] + pub use crate::stm32_tim4_monotonic; + + #[cfg(feature = "stm32_tim5")] + pub use crate::stm32_tim5_monotonic; + + #[cfg(feature = "stm32_tim15")] + pub use crate::stm32_tim15_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} + use atomic_polyfill::{AtomicU64, Ordering}; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, +}; use stm32_metapac as pac; mod _generated { @@ -48,116 +66,180 @@ mod _generated { include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } -const TIMER_HZ: u32 = 1_000_000; - #[doc(hidden)] #[macro_export] macro_rules! __internal_create_stm32_timer_interrupt { - ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + ($mono_backend:ident, $interrupt_name:ident) => { #[no_mangle] #[allow(non_snake_case)] - unsafe extern "C" fn $timer() { - $crate::stm32::$mono_timer::__tq().on_monotonic_interrupt(); + unsafe extern "C" fn $interrupt_name() { + use $crate::TimerQueueBackend; + $crate::stm32::$mono_backend::timer_queue().on_monotonic_interrupt(); } + }; +} - pub struct $timer_token; +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_stm32_timer_struct { + ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { + struct $name; - unsafe impl $crate::InterruptToken<$crate::stm32::$mono_timer> for $timer_token {} + impl $name { + /// Starts the `Monotonic`. + /// + /// - `tim_clock_hz`: `TIMx` peripheral clock frequency. + /// + /// Panics if it is impossible to achieve the desired monotonic tick rate based + /// on the given `tim_clock_hz` parameter. If that happens, adjust the desired monotonic tick rate. + /// + /// This method must be called only once. + pub fn start(tim_clock_hz: u32) { + $crate::__internal_create_stm32_timer_interrupt!($mono_backend, $timer); - $timer_token - }}; + $crate::stm32::$mono_backend::_start(tim_clock_hz, $tick_rate_hz); + } + } + + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::stm32::$mono_backend; + type Instant = $crate::fugit::Instant< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + } + + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register TIM2 interrupt for the monotonic. +/// Create a TIM2 based monotonic and register the TIM2 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim2")] #[macro_export] -macro_rules! create_stm32_tim2_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim2, TIM2, Tim2Token) - }}; +macro_rules! stm32_tim2_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim2Backend, TIM2, $tick_rate_hz); + }; } -/// Register TIM3 interrupt for the monotonic. +/// Create a TIM3 based monotonic and register the TIM3 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim3")] #[macro_export] -macro_rules! create_stm32_tim3_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim3, TIM3, Tim3Token) - }}; +macro_rules! stm32_tim3_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim3Backend, TIM3, $tick_rate_hz); + }; } -/// Register TIM4 interrupt for the monotonic. +/// Create a TIM4 based monotonic and register the TIM4 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim4")] #[macro_export] -macro_rules! create_stm32_tim4_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim4, TIM4, Tim4Token) - }}; +macro_rules! stm32_tim4_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim4Backend, TIM4, $tick_rate_hz); + }; } -/// Register TIM5 interrupt for the monotonic. +/// Create a TIM5 based monotonic and register the TIM5 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim5")] #[macro_export] -macro_rules! create_stm32_tim5_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim5, TIM5, Tim5Token) - }}; -} - -/// Register TIM12 interrupt for the monotonic. -#[cfg(feature = "stm32_tim12")] -#[macro_export] -macro_rules! create_stm32_tim12_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim12, TIM12, Tim12Token) - }}; +macro_rules! stm32_tim5_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim5Backend, TIM5, $tick_rate_hz); + }; } -/// Register TIM15 interrupt for the monotonic. +/// Create a TIM15 based monotonic and register the TIM15 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim15")] #[macro_export] -macro_rules! create_stm32_tim15_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim15, TIM15, Tim15Token) - }}; +macro_rules! stm32_tim15_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim15Backend, TIM15, $tick_rate_hz); + }; } macro_rules! make_timer { - ($mono_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Monotonic timer queue implementation. + ($backend_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// Monotonic timer backend implementation. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; use pac::$timer; static $overflow: AtomicU64 = AtomicU64::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); - impl $mono_name { - /// Starts the monotonic timer. + impl $backend_name { + /// Starts the timer. /// - /// - `tim_clock_hz`: `TIMx` peripheral clock frequency. - /// - `_interrupt_token`: Required for correct timer interrupt handling. + /// **Do not use this function directly.** /// - /// This method must be called only once. - pub fn start(tim_clock_hz: u32, _interrupt_token: impl crate::InterruptToken<Self>) { + /// Use the prelude macros instead. + pub fn _start(tim_clock_hz: u32, timer_hz: u32) { _generated::$timer::enable(); _generated::$timer::reset(); $timer.cr1().modify(|r| r.set_cen(false)); - assert!((tim_clock_hz % TIMER_HZ) == 0, "Unable to find suitable timer prescaler value!"); - let psc = tim_clock_hz / TIMER_HZ - 1; + assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!"); + let psc = tim_clock_hz / timer_hz - 1; $timer.psc().write(|r| r.set_psc(psc as u16)); // Enable full-period interrupt. $timer.dier().modify(|r| r.set_uie(true)); // Configure and enable half-period interrupt - $timer.ccr(2).write(|r| r.set_ccr($bits::MAX - ($bits::MAX >> 1))); + $timer.ccr(2).write(|r| r.set_ccr(($bits::MAX - ($bits::MAX >> 1)).into())); $timer.dier().modify(|r| r.set_ccie(2, true)); // Trigger an update event to load the prescaler value to the clock. @@ -183,73 +265,31 @@ macro_rules! make_timer { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: <Self as Monotonic>::Duration) { - $tq.delay(duration).await; - } - - /// Timeout at a specific time. - pub async fn timeout_at<F: core::future::Future>( - instant: <Self as rtic_time::Monotonic>::Instant, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after<F: core::future::Future>( - duration: <Self as Monotonic>::Duration, - future: F, - ) -> Result<F::Output, TimeoutError> { - $tq.timeout_after(duration, future).await - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: <Self as Monotonic>::Instant) { - $tq.delay_until(instant).await; - } } - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - impl Monotonic for $mono_name { - type Instant = fugit::TimerInstantU64<TIMER_HZ>; - type Duration = fugit::TimerDurationU64<TIMER_HZ>; - - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - fn now() -> Self::Instant { - Self::Instant::from_ticks(calculate_now( + fn now() -> Self::Ticks { + calculate_now( || $overflow.load(Ordering::Relaxed), || $timer.cnt().read().cnt() - )) + ) } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let now = Self::now(); // Since the timer may or may not overflow based on the requested compare val, we check how many ticks are left. - let val = match instant.checked_duration_since(now) { - None => 0, // In the past - Some(x) if x.ticks() <= ($bits::MAX as u64) => instant.duration_since_epoch().ticks() as $bits, // Will not overflow - Some(_x) => 0, // Will overflow + // `wrapping_sup` takes care of the u64 integer overflow special case. + let val = if instant.wrapping_sub(now) <= ($bits::MAX as u64) { + instant as $bits + } else { + // In the past or will overflow + 0 }; - $timer.ccr(1).write(|r| r.set_ccr(val)); + $timer.ccr(1).write(|r| r.set_ccr(val.into())); } fn clear_compare_flag() { @@ -282,24 +322,25 @@ macro_rules! make_timer { assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!"); } } + + fn timer_queue() -> &'static TimerQueue<$backend_name> { + &$tq + } } }; } #[cfg(feature = "stm32_tim2")] -make_timer!(Tim2, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ); +make_timer!(Tim2Backend, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ); #[cfg(feature = "stm32_tim3")] -make_timer!(Tim3, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ); +make_timer!(Tim3Backend, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ); #[cfg(feature = "stm32_tim4")] -make_timer!(Tim4, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ); +make_timer!(Tim4Backend, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ); #[cfg(feature = "stm32_tim5")] -make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ); - -#[cfg(feature = "stm32_tim12")] -make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ); +make_timer!(Tim5Backend, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ); #[cfg(feature = "stm32_tim15")] -make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ); +make_timer!(Tim15Backend, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ); diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index 9bd056c..cc6ea3e 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -1,93 +1,79 @@ -//! [`Monotonic`] based on Cortex-M SysTick. Note: this implementation is inefficient as it +//! [`Monotonic`](rtic_time::Monotonic) based on Cortex-M SysTick. +//! Note: this implementation is inefficient as it //! ticks and generates interrupts at a constant rate. //! -//! Currently, the following tick rates are supported: -//! -//! | Feature | Tick rate | Precision | -//! |:----------------:|----------:|----------:| -//! | (none / default) | 1 kHz | 1 ms | -//! | systick-100hz | 100 Hz | 10 ms | -//! | systick-10khz | 10 kHz | 0.1 ms | - //! # Example //! //! ``` -//! use rtic_monotonics::systick::*; +//! use rtic_monotonics::systick::prelude::*; +//! systick_monotonic!(Mono, 1_000); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let systick = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let systick_token = rtic_monotonics::create_systick_token!(); -//! +//! # //! // Start the monotonic -//! Systick::start(systick, 12_000_000, systick_token); +//! Mono::start(systick, 12_000_000); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic +//! let timestamp = Mono::now(); //! Systick::delay(100.millis()).await; //! } //! } //! ``` -use super::Monotonic; -pub use super::{TimeoutError, TimerQueue}; +/// Common definitions and traits for using the systick monotonic +pub mod prelude { + pub use crate::systick_monotonic; + + pub use crate::Monotonic; + + cfg_if::cfg_if! { + if #[cfg(feature = "systick-64bit")] { + pub use fugit::{self, ExtU64, ExtU64Ceil}; + } else { + pub use fugit::{self, ExtU32, ExtU32Ceil}; + } + } +} + +pub use cortex_m::peripheral::SYST; + use atomic_polyfill::Ordering; -use core::future::Future; -use cortex_m::peripheral::SYST; -pub use fugit; +use rtic_time::timer_queue::TimerQueue; + +use crate::TimerQueueBackend; + cfg_if::cfg_if! { if #[cfg(feature = "systick-64bit")] { - pub use fugit::{ExtU64, ExtU64Ceil}; use atomic_polyfill::AtomicU64; static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0); } else { - pub use fugit::{ExtU32, ExtU32Ceil}; use atomic_polyfill::AtomicU32; static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); } } -static SYSTICK_TIMER_QUEUE: TimerQueue<Systick> = TimerQueue::new(); -// Features should be additive, here systick-100hz gets picked if both -// `systick-100hz` and `systick-10khz` are enabled. +static SYSTICK_TIMER_QUEUE: TimerQueue<SystickBackend> = TimerQueue::new(); -cfg_if::cfg_if! { - if #[cfg(feature = "systick-100hz")] - { - const TIMER_HZ: u32 = 100; - } else if #[cfg(feature = "systick-10khz")] - { - const TIMER_HZ: u32 = 10_000; - } else { - // Default case is 1 kHz - const TIMER_HZ: u32 = 1_000; - } -} - -/// Systick implementing [`Monotonic`] which runs at 1 kHz, 100Hz or 10 kHz. -pub struct Systick; +/// Systick based [`TimerQueueBackend`]. +pub struct SystickBackend; -impl Systick { - /// Start a `Monotonic` based on SysTick. - /// - /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from - /// the clock generation function of the used HAL. +impl SystickBackend { + /// Starts the monotonic timer. /// - /// Notice that the actual rate of the timer is a best approximation based on the given - /// `sysclk` and `TIMER_HZ`. + /// **Do not use this function directly.** /// - /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. - pub fn start( - mut systick: cortex_m::peripheral::SYST, - sysclk: u32, - _interrupt_token: impl crate::InterruptToken<Self>, - ) { - // + TIMER_HZ / 2 provides round to nearest instead of round to 0. - // - 1 as the counter range is inclusive [0, reload] - let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; + /// Use the prelude macros instead. + pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) { + assert!( + (sysclk % timer_hz) == 0, + "timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency." + ); + let reload = sysclk / timer_hz - 1; assert!(reload <= 0x00ff_ffff); assert!(reload > 0); @@ -98,7 +84,7 @@ impl Systick { systick.enable_interrupt(); systick.enable_counter(); - SYSTICK_TIMER_QUEUE.initialize(Systick {}); + SYSTICK_TIMER_QUEUE.initialize(SystickBackend {}); } fn systick() -> SYST { @@ -106,67 +92,24 @@ impl Systick { } } -// Forward timerqueue interface -impl Systick { - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<Systick> { - &SYSTICK_TIMER_QUEUE - } - - /// Timeout at a specific time. - pub async fn timeout_at<F: Future>( - instant: <Self as Monotonic>::Instant, - future: F, - ) -> Result<F::Output, TimeoutError> { - SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after<F: Future>( - duration: <Self as Monotonic>::Duration, - future: F, - ) -> Result<F::Output, TimeoutError> { - SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: <Self as Monotonic>::Duration) { - SYSTICK_TIMER_QUEUE.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: <Self as Monotonic>::Instant) { - SYSTICK_TIMER_QUEUE.delay_until(instant).await; - } -} - -impl Monotonic for Systick { +impl TimerQueueBackend for SystickBackend { cfg_if::cfg_if! { if #[cfg(feature = "systick-64bit")] { - type Instant = fugit::TimerInstantU64<TIMER_HZ>; - type Duration = fugit::TimerDurationU64<TIMER_HZ>; + type Ticks = u64; } else { - type Instant = fugit::TimerInstantU32<TIMER_HZ>; - type Duration = fugit::TimerDurationU32<TIMER_HZ>; + type Ticks = u32; } } - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { if Self::systick().has_wrapped() { SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); } - Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed)) + SYSTICK_CNT.load(Ordering::Relaxed) } - fn set_compare(_: Self::Instant) { + fn set_compare(_: Self::Ticks) { // No need to do something here, we get interrupts anyway. } @@ -184,39 +127,66 @@ impl Monotonic for Systick { } } - fn enable_timer() {} - - fn disable_timer() {} -} - -cfg_if::cfg_if! { - if #[cfg(feature = "systick-64bit")] { - rtic_time::embedded_hal_delay_impl_fugit64!(Systick); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!(Systick); - } else { - rtic_time::embedded_hal_delay_impl_fugit32!(Systick); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit32!(Systick); + fn timer_queue() -> &'static TimerQueue<Self> { + &SYSTICK_TIMER_QUEUE } } -/// Register the Systick interrupt for the monotonic. +/// Create a Systick based monotonic and register the Systick interrupt for it. +/// +/// See [`crate::systick`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// Can be omitted; defaults to 1kHz. #[macro_export] -macro_rules! create_systick_token { - () => {{ - #[no_mangle] - #[allow(non_snake_case)] - unsafe extern "C" fn SysTick() { - $crate::systick::Systick::__tq().on_monotonic_interrupt(); +macro_rules! systick_monotonic { + ($name:ident) => { + $crate::systick_monotonic($name, 1_000); + }; + ($name:ident, $tick_rate_hz:expr) => { + /// A `Monotonic` based on SysTick. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from + /// the clock generation function of the used HAL. + /// + /// Panics if it is impossible to achieve the desired monotonic tick rate based + /// on the given `sysclk` parameter. If that happens, adjust the desired monotonic tick rate. + /// + /// This method must be called only once. + pub fn start(systick: $crate::systick::SYST, sysclk: u32) { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn SysTick() { + use $crate::TimerQueueBackend; + $crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt(); + } + + $crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz); + } } - pub struct SystickToken; - - unsafe impl $crate::InterruptToken<$crate::systick::Systick> for SystickToken {} + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::systick::SystickBackend; + type Instant = $crate::fugit::Instant< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + <Self::Backend as $crate::TimerQueueBackend>::Ticks, + 1, + { $tick_rate_hz }, + >; + } - SystickToken - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } |
