embedded_c_sdk_bind_hal/
tick.rs

1use crate::ll_api::ll_cmd::*;
2use embedded_hal::delay::DelayNs;
3use fugit::{Duration, TimerDurationU32};
4use portable_atomic::{AtomicU32, Ordering};
5
6pub const TICK_FREQ_HZ: u32 = crate::tick_freq_hz::TICK_FREQ_HZ;
7
8#[cfg(not(feature = "tick-size-64bit"))]
9pub type TickType = u32;
10#[cfg(feature = "tick-size-64bit")]
11pub type TickType = u64;
12
13static SYS_TICK_0: AtomicU32 = AtomicU32::new(0);
14#[cfg(feature = "tick-size-64bit")]
15static SYS_TICK_1: AtomicU32 = AtomicU32::new(0);
16
17pub trait HalTickHandler {
18    unsafe fn on_sys_tick_interrupt();
19}
20
21impl HalTickHandler for Tick {
22    /// This function is called by a hardware interrupt to update the system's tick count.
23    /// Depending on the build features, it supports either 32-bit or 64-bit tick counters.
24    ///
25    #[inline]
26    unsafe fn on_sys_tick_interrupt() {
27        // Increment the low-order 32-bit tick counter atomically.
28        #[cfg(any(feature = "tick-size-64bit", feature = "embassy"))]
29        let sys_tick = SYS_TICK_0.fetch_add(1, Ordering::Relaxed);
30
31        // Increment the low-order 32-bit tick counter using a method compatible with non-64bit environments.
32        #[cfg(not(any(feature = "tick-size-64bit", feature = "embassy")))]
33        SYS_TICK_0.add(1, Ordering::Relaxed);
34
35        // Handle 64-bit tick overflow and update the high-order 32-bit tick counter.
36        #[cfg(feature = "tick-size-64bit")]
37        let sys_tick = if sys_tick == u32::MAX {
38            let tick_1 = SYS_TICK_1.fetch_add(1, Ordering::Release);
39            ((tick_1 as u64) << 32) | (sys_tick as u64)
40        } else {
41            let tick_1 = SYS_TICK_1.load(Ordering::Relaxed);
42            ((tick_1 as u64) << 32) | (sys_tick as u64)
43        };
44
45        // If the 'embassy' feature is enabled, check for any alarms that need to be triggered.
46        #[cfg(feature = "embassy")]
47        tick_time_driver::check_alarm(sys_tick);
48    }
49}
50
51#[no_mangle]
52#[deprecated(since = "0.7.3", note = "Please use `sys_tick_handler!()` instead")]
53unsafe extern "C" fn sys_tick_inc() {
54    Tick::on_sys_tick_interrupt();
55}
56
57/// Represents a point in time as measured by the system tick counter.
58///
59/// This struct is used to keep track of time in terms of ticks, which can be compared or used to measure elapsed time.
60#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
61pub struct Tick(TickType);
62
63impl Tick {
64    /// Returns the current system tick value.
65    ///
66    /// # Examples
67    /// ```
68    /// let current_tick = Tick::now();
69    /// ```
70    pub fn now() -> Self {
71        // For 64-bit ticks, ensure we get a consistent snapshot of both high and low parts.
72        #[cfg(feature = "tick-size-64bit")]
73        loop {
74            let t0 = SYS_TICK_1.load(Ordering::SeqCst);
75            let t = SYS_TICK_0.load(Ordering::SeqCst);
76            let t1 = SYS_TICK_1.load(Ordering::SeqCst);
77            if t0 == t1 {
78                break Tick(((t0 as u64) << 32) | (t as u64));
79            }
80        }
81        // For 32-bit ticks, simply load the tick counter.
82        #[cfg(not(feature = "tick-size-64bit"))]
83        Tick(SYS_TICK_0.load(Ordering::Relaxed))
84    }
85
86    /// Computes the number of ticks elapsed since the creation of this `Tick` instance.
87    ///
88    /// # Examples
89    /// ```
90    /// let start_tick = Tick::now();
91    /// let elapsed_ticks = start_tick.elapsed();
92    /// ```
93    pub fn elapsed(self) -> TickType {
94        if let Some(tick) = Self::now().0.checked_sub(self.0) {
95            tick
96        } else {
97            TickType::MAX
98        }
99    }
100
101    /// Creates a new `Tick` instance with the given value.
102    ///
103    /// # Arguments
104    /// * `value` - The value to initialize the `Tick` with.
105    ///
106    /// # Returns
107    /// * A new `Tick` instance.
108    #[inline]
109    pub const fn with_value(value: TickType) -> Self {
110        Tick(value)
111    }
112
113    /// Retrieves the current tick value.
114    ///
115    /// # Returns
116    /// * The current tick value.
117    #[inline]
118    pub fn tick() -> TickType {
119        Self::now().0
120    }
121
122    /// Calculates the elapsed time since the creation of the `Tick` instance.
123    ///
124    /// # Returns
125    /// * A `Duration` representing the elapsed time in ticks.
126    pub fn elapsed_time(self) -> Duration<TickType, 1, TICK_FREQ_HZ> {
127        let tick = if let Some(res) = Self::now().0.checked_sub(self.0) {
128            res
129        } else {
130            TickType::MAX
131        };
132
133        Duration::<TickType, 1, TICK_FREQ_HZ>::from_ticks(tick)
134    }
135
136    /// Checks if the specified duration has elapsed since the last check.
137    ///
138    /// # Arguments
139    /// * `duration` - The duration to check for in ticks.
140    ///
141    /// # Returns
142    /// * `true` if the specified duration has elapsed, `false` otherwise.
143    pub fn every(&mut self, duration: TickType) -> bool {
144        let timeout = if let Some(elapsed) = Self::now().0.checked_sub(self.0) {
145            if elapsed >= duration {
146                true
147            } else {
148                false
149            }
150        } else {
151            true
152        };
153
154        if timeout {
155            self.0 = Self::now().0;
156        }
157        return timeout;
158    }
159
160    /// Resets the tick counter to the current system tick value.
161    pub fn reset(&mut self) {
162        self.0 = Self::now().0;
163    }
164}
165
166impl core::ops::Add for Tick {
167    type Output = Self;
168
169    fn add(self, rhs: Self) -> Self::Output {
170        Tick(self.0 + rhs.0)
171    }
172}
173
174impl core::ops::Sub for Tick {
175    type Output = Self;
176
177    fn sub(self, rhs: Self) -> Self::Output {
178        Tick(self.0 - rhs.0)
179    }
180}
181
182/// A delay utility that allows for precise timing delays based on a specified tick frequency.
183///
184/// This struct provides methods to create a delay object and then use it to delay for a specific amount of time.
185/// The delay is implemented using the system's tick counter and can be configured for different frequencies.
186///
187/// # Examples
188/// ```
189/// let mut delay = Delay::new(); // Create a delay object for a 1000 Hz tick frequency.
190/// delay.delay_ms(500); // Delay for 500 milliseconds.
191/// ```
192#[derive(Debug, Clone, Copy)]
193pub struct Delay;
194
195impl Delay {
196    /// Creates a new `Delay` instance for the specified tick frequency.
197    ///
198    /// # Examples
199    /// ```
200    /// let mut delay = Delay::new(); // Create a delay object for a 1000 Hz tick frequency.
201    /// ```
202    pub const fn new() -> Self {
203        Delay
204    }
205}
206
207/// Trait implementation for delaying in nanoseconds.
208impl DelayNs for Delay {
209    /// Delays for a specified number of nanoseconds.
210    ///
211    /// # Examples
212    /// ```
213    /// let mut delay = Delay::new();
214    /// delay.delay_ns(1_000_000); // Delay for 1 millisecond (1,000,000 nanoseconds).
215    /// ```
216    #[inline]
217    fn delay_ns(&mut self, ns: u32) {
218        ll_invoke_inner!(INVOKE_ID_DELAY_NANO, ns);
219    }
220
221    /// Delays for a specified number of milliseconds.
222    ///
223    /// # Examples
224    /// ```
225    /// let mut delay = Delay::new();
226    /// delay.delay_ms(500); // Delay for 500 milliseconds.
227    /// ```
228    #[cfg(feature = "tick-based-msdelay")]
229    #[inline]
230    fn delay_ms(&mut self, ms: u32) {
231        let ms_tick = TimerDurationU32::<TICK_FREQ_HZ>::millis(ms).ticks();
232        let start = Tick::now();
233        loop {
234            unsafe {
235                core::arch::asm!("wfi");
236            }
237            if (start.elapsed() as u32) >= ms_tick {
238                break;
239            }
240        }
241    }
242}
243
244#[cfg(feature = "embassy")]
245mod tick_time_driver {
246    use core::cell::{Cell, RefCell};
247    use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
248    use embassy_sync::blocking_mutex::Mutex;
249    use embassy_time_driver::Driver;
250    use embassy_time_queue_utils::Queue;
251
252    struct AlarmState {
253        timestamp: Cell<super::TickType>,
254    }
255    unsafe impl Send for AlarmState {}
256
257    struct TimerDriver {
258        alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
259        queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
260    }
261
262    embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
263        alarms:  Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {
264            timestamp: Cell::new(0),
265        }),
266        queue: Mutex::new(RefCell::new(Queue::new()))
267    });
268
269    #[inline(always)]
270    pub fn check_alarm(curr_tick: super::TickType) {
271        DRIVER.check_alarm(curr_tick);
272    }
273
274    impl TimerDriver {
275        /// Internal function to check and trigger alarms.
276        ///
277        /// This function enters a critical section to safely access shared resources and checks if there is an alarm
278        /// set to go off at the current tick. If an alarm is due, it resets the alarm timestamp and invokes the callback.
279        fn check_alarm(&self, curr_tick: super::TickType) {
280            critical_section::with(|cs| {
281                let alarm = &self.alarms.borrow(cs);
282                let mut timestamp = alarm.timestamp.get();
283                while timestamp <= curr_tick {
284                    let mut queue = self.queue.borrow(cs).borrow_mut();
285                    timestamp = queue.next_expiration(curr_tick as u64) as super::TickType; //get next timestamp
286                    alarm.timestamp.set(timestamp); //set next alarm
287                }
288            });
289        }
290    }
291
292    impl Driver for TimerDriver {
293        fn now(&self) -> u64 {
294            super::Tick::now().0 as u64
295        }
296
297        fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
298            critical_section::with(|cs| {
299                let mut queue = self.queue.borrow(cs).borrow_mut();
300
301                if queue.schedule_wake(at, waker) {
302                    let alarm = &self.alarms.borrow(cs);
303                    let now = self.now();
304                    loop {
305                        let timestamp = queue.next_expiration(now) as super::TickType; //get next timestamp
306                        alarm.timestamp.set(timestamp); //set next alarm
307                        if timestamp > now as super::TickType {
308                            break;
309                        }
310                    }
311                }
312            })
313        }
314    }
315}