systick_timer/
embassy_driver.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use core::cell::RefCell;
4use cortex_m::{
5    interrupt::{self, Mutex},
6    peripheral::SYST,
7};
8
9struct Wakeup {
10    wakeup_at: u64,
11    waker: core::task::Waker,
12}
13
14/// Very basic Embassy time driver that uses the SysTick timer.
15///
16/// Wakeups are stored in a fixed-size array
17///
18/// The driver has to be a static instance, create it with:
19///
20/// ```
21/// embassy_time_driver::time_driver_impl!(static DRIVER: SystickDriver<4>
22///     = SystickDriver::new(48_000_000, 47999));
23/// ```
24///
25pub struct SystickDriver<const N: usize> {
26    wakeup_at: Mutex<RefCell<[Option<Wakeup>; N]>>,
27    timer: crate::Timer,
28}
29
30impl<const N: usize> SystickDriver<N> {
31    /// SystickDriver constructor.
32    ///
33    /// # Arguments
34    ///
35    /// * `systick_freq` - The frequency of the SysTick timer in Hz.
36    /// * `reload_value` - The reload value for the SysTick timer.
37    ///
38    ///  Note the tick frequency is configured to embassy_time_driver::TICK_HZ.
39    ///
40    pub const fn new(systick_freq: u64, reload_value: u32) -> Self {
41        let timer = crate::Timer::new(embassy_time_driver::TICK_HZ, reload_value, systick_freq);
42        Self {
43            wakeup_at: Mutex::new(RefCell::new([const { None }; N])),
44            timer: timer,
45        }
46    }
47
48    /// Create a new SystickDriver with a default reload value that matches
49    /// the interrupt frequency to embassy_time_driver::TICK_HZ.
50    ///
51    /// The default reload value is calculated as (systick_freq / embassy_time_driver::TICK_HZ) - 1.
52    ///
53    /// # Arguments
54    ///
55    /// * `systick_freq` - The frequency of the SysTick timer in Hz.
56    ///
57    pub const fn new_default(systick_freq: u64) -> Self {
58        let reload = (systick_freq / embassy_time_driver::TICK_HZ) - 1;
59        Self::new(systick_freq, reload as u32)
60    }
61
62    fn maybe_wake(&self) {
63        interrupt::free(|cs| {
64            let mutex_borrow = &self.wakeup_at.borrow(cs);
65            for slot in mutex_borrow.borrow_mut().iter_mut() {
66                let mut cleared = false;
67                if let Some(wakeup) = slot {
68                    if self.timer.now() >= wakeup.wakeup_at {
69                        wakeup.waker.wake_by_ref();
70                        cleared = true;
71                    }
72                }
73                if cleared {
74                    *slot = None;
75                }
76            }
77        })
78    }
79
80    pub fn start(&self, syst: &mut SYST) {
81        self.timer.start(syst);
82    }
83
84    /// Call this from the SysTick interrupt handler.
85    pub fn systick_interrupt(&self) {
86        self.timer.systick_handler();
87        self.maybe_wake();
88    }
89}
90
91impl<const N: usize> embassy_time_driver::Driver for SystickDriver<N> {
92    fn now(&self) -> u64 {
93        self.timer.now()
94    }
95
96    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
97        interrupt::free(|cs| {
98            let mutex_borrow = self.wakeup_at.borrow(cs);
99            let mut found = false;
100            for slot in mutex_borrow.borrow_mut().iter_mut() {
101                if slot.is_none() {
102                    *slot = Some(Wakeup {
103                        wakeup_at: at,
104                        waker: waker.clone(),
105                    });
106                    found = true;
107                    break;
108                }
109            }
110            if !found {
111                panic!("No free wakeup slots");
112            }
113        })
114    }
115}
116
117#[cfg(feature = "embassy-defaults")]
118embassy_time_driver::time_driver_impl!(static DRIVER: SystickDriver<4> = SystickDriver::new(8_000_000, 7_999));
119
120#[cfg(feature = "embassy-defaults")]
121#[cortex_m_rt::exception]
122fn SysTick() {
123    DRIVER.systick_interrupt();
124}