From 8c23e178f3838bcdd13662a2ffefd39ec144e869 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Thu, 11 Apr 2024 00:00:38 +0200 Subject: Monotonic rewrite (#874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rework timer_queue and monotonic architecture Goals: * make Monotonic purely internal * make Monotonic purely tick passed, no fugit involved * create a wrapper struct in the user's code via a macro that then converts the "now" from the tick based monotonic to a fugit based timestamp We need to proxy the delay functions of the timer queue anyway, so we could simply perform the conversion in those proxy functions. * Update cargo.lock * Update readme of rtic-time * CI: ESP32: Redact esp_image: Too volatile * Fixup: Changelog double entry rebase mistake --------- Co-authored-by: Henrik Tjäder --- rtic-monotonics/src/systick.rs | 236 ++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 133 deletions(-) (limited to 'rtic-monotonics/src/systick.rs') 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 = 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 = 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, - ) { - // + 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_TIMER_QUEUE - } - - /// Timeout at a specific time. - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - SYSTICK_TIMER_QUEUE.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::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; - type Duration = fugit::TimerDurationU64; + type Ticks = u64; } else { - type Instant = fugit::TimerInstantU32; - type Duration = fugit::TimerDurationU32; + 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 { + &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< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + ::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); + }; } -- cgit v1.2.3