systick_monotonic/
lib.rs

1//! # [`Monotonic`] implementation based on SysTick
2//!
3//! Uses [`fugit`] as underlying time library.
4//!
5//! [`fugit`]: https://docs.rs/crate/fugit
6//! [`Monotonic`]: https://docs.rs/rtic-monotonic
7
8#![no_std]
9
10use cortex_m::peripheral::{syst::SystClkSource, SYST};
11pub use fugit::{self, ExtU64};
12use rtic_monotonic::Monotonic;
13
14/// Systick implementing `rtic_monotonic::Monotonic` which runs at a
15/// settable rate using the `TIMER_HZ` parameter.
16pub struct Systick<const TIMER_HZ: u32> {
17    systick: SYST,
18    cnt: u64,
19}
20
21impl<const TIMER_HZ: u32> Systick<TIMER_HZ> {
22    /// Provide a new `Monotonic` based on SysTick.
23    ///
24    /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
25    /// the clock generation function of the used HAL.
26    ///
27    /// Notice that the actual rate of the timer is a best approximation based on the given
28    /// `sysclk` and `TIMER_HZ`.
29    pub fn new(mut systick: SYST, sysclk: u32) -> Self {
30        // + TIMER_HZ / 2 provides round to nearest instead of round to 0.
31        // - 1 as the counter range is inclusive [0, reload]
32        let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1;
33
34        assert!(reload <= 0x00ff_ffff);
35        assert!(reload > 0);
36
37        systick.disable_counter();
38        systick.set_clock_source(SystClkSource::Core);
39        systick.set_reload(reload);
40
41        Systick { systick, cnt: 0 }
42    }
43}
44
45impl<const TIMER_HZ: u32> Monotonic for Systick<TIMER_HZ> {
46    const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
47
48    type Instant = fugit::TimerInstantU64<TIMER_HZ>;
49    type Duration = fugit::TimerDurationU64<TIMER_HZ>;
50
51    fn now(&mut self) -> Self::Instant {
52        if self.systick.has_wrapped() {
53            self.cnt = self.cnt.wrapping_add(1);
54        }
55
56        Self::Instant::from_ticks(self.cnt)
57    }
58
59    unsafe fn reset(&mut self) {
60        self.systick.clear_current();
61        self.systick.enable_counter();
62    }
63
64    #[inline(always)]
65    fn set_compare(&mut self, _val: Self::Instant) {
66        // No need to do something here, we get interrupts anyway.
67    }
68
69    #[inline(always)]
70    fn clear_compare_flag(&mut self) {
71        // NOOP with SysTick interrupt
72    }
73
74    #[inline(always)]
75    fn zero() -> Self::Instant {
76        Self::Instant::from_ticks(0)
77    }
78
79    #[inline(always)]
80    fn on_interrupt(&mut self) {
81        if self.systick.has_wrapped() {
82            self.cnt = self.cnt.wrapping_add(1);
83        }
84    }
85}