rtic_monotonics/
systick.rs

1//! [`Monotonic`](rtic_time::Monotonic) based on Cortex-M SysTick.
2//!
3//! Note: this implementation is inefficient as it
4//! ticks and generates interrupts at a constant rate.
5//!
6//! # Example
7//!
8//! ```
9//! use rtic_monotonics::systick::prelude::*;
10//!
11//! // Create the type `Mono`. It will manage the SysTick timer, and use it to
12//! // generate 1000 interrupts per second.
13//! systick_monotonic!(Mono, 1_000);
14//!
15//! fn init() {
16//!     let core_peripherals = cortex_m::Peripherals::take().unwrap();
17//!     // Start the monotonic using the cortex-m crate's Systick driver.
18//!     // We tell it we have a system clock of 12 MHz.
19//!     Mono::start(core_peripherals.SYST, 12_000_000);
20//! }
21//!
22//! async fn usage() {
23//!     loop {
24//!          // You can use the monotonic to get the time...
25//!          let timestamp = Mono::now();
26//!          // ...and you can use it to add a delay to this async function
27//!          Mono::delay(100.millis()).await;
28//!     }
29//! }
30//! ```
31
32/// Common definitions and traits for using the systick monotonic
33pub mod prelude {
34    pub use crate::systick_monotonic;
35
36    pub use crate::Monotonic;
37
38    cfg_if::cfg_if! {
39        if #[cfg(feature = "systick-64bit")] {
40            pub use fugit::{self, ExtU64, ExtU64Ceil};
41        } else {
42            pub use fugit::{self, ExtU32, ExtU32Ceil};
43        }
44    }
45}
46
47pub use cortex_m::peripheral::SYST;
48
49use portable_atomic::Ordering;
50use rtic_time::timer_queue::TimerQueue;
51
52use crate::TimerQueueBackend;
53
54cfg_if::cfg_if! {
55    if #[cfg(feature = "systick-64bit")] {
56        use portable_atomic::AtomicU64;
57        static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0);
58    } else {
59        use portable_atomic::AtomicU32;
60        static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0);
61    }
62}
63
64static SYSTICK_TIMER_QUEUE: TimerQueue<SystickBackend> = TimerQueue::new();
65
66/// Systick based [`TimerQueueBackend`].
67pub struct SystickBackend;
68
69impl SystickBackend {
70    /// Starts the monotonic timer.
71    ///
72    /// **Do not use this function directly.**
73    ///
74    /// Use the prelude macros instead.
75    pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) {
76        assert!(
77            (sysclk % timer_hz) == 0,
78            "timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency."
79        );
80        let reload = sysclk / timer_hz - 1;
81
82        assert!(reload <= 0x00ff_ffff);
83        assert!(reload > 0);
84
85        systick.disable_counter();
86        systick.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
87        systick.set_reload(reload);
88        systick.enable_interrupt();
89        systick.enable_counter();
90
91        SYSTICK_TIMER_QUEUE.initialize(SystickBackend {});
92    }
93
94    fn systick() -> SYST {
95        unsafe { core::mem::transmute::<(), SYST>(()) }
96    }
97}
98
99impl TimerQueueBackend for SystickBackend {
100    cfg_if::cfg_if! {
101        if #[cfg(feature = "systick-64bit")] {
102            type Ticks = u64;
103        } else {
104            type Ticks = u32;
105        }
106    }
107
108    fn now() -> Self::Ticks {
109        if Self::systick().has_wrapped() {
110            SYSTICK_CNT.fetch_add(1, Ordering::AcqRel);
111        }
112
113        SYSTICK_CNT.load(Ordering::Relaxed)
114    }
115
116    fn set_compare(_: Self::Ticks) {
117        // No need to do something here, we get interrupts anyway.
118    }
119
120    fn clear_compare_flag() {
121        // NOOP with SysTick interrupt
122    }
123
124    fn pend_interrupt() {
125        cortex_m::peripheral::SCB::set_pendst();
126    }
127
128    fn on_interrupt() {
129        if Self::systick().has_wrapped() {
130            SYSTICK_CNT.fetch_add(1, Ordering::AcqRel);
131        }
132    }
133
134    fn timer_queue() -> &'static TimerQueue<Self> {
135        &SYSTICK_TIMER_QUEUE
136    }
137}
138
139/// Create a Systick based monotonic and register the Systick interrupt for it.
140///
141/// This macro expands to produce a new type called `$name`, which has a `fn
142/// start()` function for you to call. The type has an implementation of the
143/// `rtic_monotonics::TimerQueueBasedMonotonic` trait, the
144/// `embedded_hal::delay::DelayNs` trait and the
145/// `embedded_hal_async::delay::DelayNs` trait.
146///
147/// This macro also produces an interrupt handler for the SysTick interrupt, by
148/// creating an `extern "C" fn SysTick() { ... }`.
149///
150/// See [`crate::systick`] for more details.
151///
152/// # Arguments
153///
154/// * `name` - The name that the monotonic type will have.
155/// * `tick_rate_hz` - The tick rate of the timer peripheral.
156///   Can be omitted; defaults to 1kHz.
157#[macro_export]
158macro_rules! systick_monotonic {
159    ($name:ident) => {
160        $crate::systick_monotonic!($name, 1_000);
161    };
162    ($name:ident, $tick_rate_hz:expr) => {
163        /// A `Monotonic` based on SysTick.
164        pub struct $name;
165
166        impl $name {
167            /// Starts the `Monotonic`.
168            ///
169            /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
170            /// the clock generation function of the used HAL.
171            ///
172            /// Panics if it is impossible to achieve the desired monotonic tick rate based
173            /// on the given `sysclk` parameter. If that happens, adjust the desired monotonic tick rate.
174            ///
175            /// This method must be called only once.
176            pub fn start(systick: $crate::systick::SYST, sysclk: u32) {
177                #[no_mangle]
178                #[allow(non_snake_case)]
179                unsafe extern "C" fn SysTick() {
180                    use $crate::TimerQueueBackend;
181                    $crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt();
182                }
183
184                $crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz);
185            }
186        }
187
188        impl $crate::TimerQueueBasedMonotonic for $name {
189            type Backend = $crate::systick::SystickBackend;
190            type Instant = $crate::fugit::Instant<
191                <Self::Backend as $crate::TimerQueueBackend>::Ticks,
192                1,
193                { $tick_rate_hz },
194            >;
195            type Duration = $crate::fugit::Duration<
196                <Self::Backend as $crate::TimerQueueBackend>::Ticks,
197                1,
198                { $tick_rate_hz },
199            >;
200        }
201
202        $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
203        $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
204    };
205}