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#[inline]
53#[deprecated(since = "0.7.3", note = "Please use `sys_tick_handler!()` instead")]
54unsafe extern "C" fn sys_tick_inc() {
55    Tick::on_sys_tick_interrupt();
56}
57
58/// Represents a point in time as measured by the system tick counter.
59///
60/// This struct is used to keep track of time in terms of ticks, which can be compared or used to measure elapsed time.
61#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
62pub struct Tick(TickType);
63
64impl Tick {
65    /// Returns the current system tick value.
66    ///
67    /// # Examples
68    /// ```
69    /// let current_tick = Tick::now();
70    /// ```
71    pub fn now() -> Self {
72        // For 64-bit ticks, ensure we get a consistent snapshot of both high and low parts.
73        #[cfg(feature = "tick-size-64bit")]
74        loop {
75            let t0 = SYS_TICK_1.load(Ordering::SeqCst);
76            let t = SYS_TICK_0.load(Ordering::SeqCst);
77            let t1 = SYS_TICK_1.load(Ordering::SeqCst);
78            if t0 == t1 {
79                break Tick(((t0 as u64) << 32) | (t as u64));
80            }
81        }
82        // For 32-bit ticks, simply load the tick counter.
83        #[cfg(not(feature = "tick-size-64bit"))]
84        Tick(SYS_TICK_0.load(Ordering::Relaxed))
85    }
86
87    /// Computes the number of ticks elapsed since the creation of this `Tick` instance.
88    ///
89    /// # Examples
90    /// ```
91    /// let start_tick = Tick::now();
92    /// let elapsed_ticks = start_tick.elapsed();
93    /// ```
94    pub fn elapsed(self) -> TickType {
95        if let Some(tick) = Self::now().0.checked_sub(self.0) {
96            tick
97        } else {
98            TickType::MAX
99        }
100    }
101
102    /// Creates a new `Tick` instance with the given value.
103    ///
104    /// # Arguments
105    /// * `value` - The value to initialize the `Tick` with.
106    ///
107    /// # Returns
108    /// * A new `Tick` instance.
109    #[inline]
110    pub const fn with_value(value: TickType) -> Self {
111        Tick(value)
112    }
113
114    /// Retrieves the current tick value.
115    ///
116    /// # Returns
117    /// * The current tick value.
118    #[inline]
119    pub fn tick() -> TickType {
120        Self::now().0
121    }
122
123    /// Calculates the elapsed time since the creation of the `Tick` instance.
124    ///
125    /// # Returns
126    /// * A `Duration` representing the elapsed time in ticks.
127    pub fn elapsed_time(self) -> Duration<TickType, 1, TICK_FREQ_HZ> {
128        let tick = if let Some(res) = Self::now().0.checked_sub(self.0) {
129            res
130        } else {
131            TickType::MAX
132        };
133
134        Duration::<TickType, 1, TICK_FREQ_HZ>::from_ticks(tick)
135    }
136}
137
138impl core::ops::Add for Tick {
139    type Output = Self;
140
141    fn add(self, rhs: Self) -> Self::Output {
142        Tick(self.0 + rhs.0)
143    }
144}
145
146impl core::ops::Sub for Tick {
147    type Output = Self;
148
149    fn sub(self, rhs: Self) -> Self::Output {
150        Tick(self.0 - rhs.0)
151    }
152}
153
154/// A delay utility that allows for precise timing delays based on a specified tick frequency.
155///
156/// This struct provides methods to create a delay object and then use it to delay for a specific amount of time.
157/// The delay is implemented using the system's tick counter and can be configured for different frequencies.
158///
159/// # Examples
160/// ```
161/// let mut delay = Delay::new(); // Create a delay object for a 1000 Hz tick frequency.
162/// delay.delay_ms(500); // Delay for 500 milliseconds.
163/// ```
164#[derive(Debug, Clone, Copy)]
165pub struct Delay;
166
167impl Delay {
168    /// Creates a new `Delay` instance for the specified tick frequency.
169    ///
170    /// # Examples
171    /// ```
172    /// let mut delay = Delay::new(); // Create a delay object for a 1000 Hz tick frequency.
173    /// ```
174    pub const fn new() -> Self {
175        Delay
176    }
177}
178
179/// Trait implementation for delaying in nanoseconds.
180impl DelayNs for Delay {
181    /// Delays for a specified number of nanoseconds.
182    ///
183    /// # Examples
184    /// ```
185    /// let mut delay = Delay::new();
186    /// delay.delay_ns(1_000_000); // Delay for 1 millisecond (1,000,000 nanoseconds).
187    /// ```
188    #[inline]
189    fn delay_ns(&mut self, ns: u32) {
190        ll_invoke_inner!(INVOKE_ID_DELAY_NANO, ns);
191    }
192
193    /// Delays for a specified number of milliseconds.
194    ///
195    /// # Examples
196    /// ```
197    /// let mut delay = Delay::new();
198    /// delay.delay_ms(500); // Delay for 500 milliseconds.
199    /// ```
200    #[cfg(feature = "tick-based-msdelay")]
201    #[inline]
202    fn delay_ms(&mut self, ms: u32) {
203        let ms_tick = TimerDurationU32::<TICK_FREQ_HZ>::millis(ms).ticks();
204        let start = Tick::now();
205        loop {
206            unsafe {
207                core::arch::asm!("wfi");
208            }
209            if (start.elapsed() as u32) >= ms_tick {
210                break;
211            }
212        }
213    }
214}
215
216#[cfg(feature = "embassy")]
217mod tick_time_driver {
218    use core::cell::{Cell, RefCell};
219    use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
220    use embassy_sync::blocking_mutex::Mutex;
221    use embassy_time_driver::Driver;
222    use embassy_time_queue_utils::Queue;
223
224    struct AlarmState {
225        timestamp: Cell<super::TickType>,
226    }
227    unsafe impl Send for AlarmState {}
228
229    struct TimerDriver {
230        alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
231        queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
232    }
233
234    embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
235        alarms:  Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {
236            timestamp: Cell::new(0),
237        }),
238        queue: Mutex::new(RefCell::new(Queue::new()))
239    });
240
241    #[inline(always)]
242    pub fn check_alarm(curr_tick: super::TickType) {
243        DRIVER.check_alarm(curr_tick);
244    }
245
246    impl TimerDriver {
247        /// Internal function to check and trigger alarms.
248        ///
249        /// This function enters a critical section to safely access shared resources and checks if there is an alarm
250        /// set to go off at the current tick. If an alarm is due, it resets the alarm timestamp and invokes the callback.
251        fn check_alarm(&self, curr_tick: super::TickType) {
252            critical_section::with(|cs| {
253                let alarm = &self.alarms.borrow(cs);
254                let mut timestamp = alarm.timestamp.get();
255                while timestamp <= curr_tick {
256                    let mut queue = self.queue.borrow(cs).borrow_mut();
257                    timestamp = queue.next_expiration(curr_tick as u64) as super::TickType; //get next timestamp
258                    alarm.timestamp.set(timestamp); //set next alarm
259                }
260            });
261        }
262    }
263
264    impl Driver for TimerDriver {
265        fn now(&self) -> u64 {
266            super::Tick::now().0 as u64
267        }
268
269        fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
270            critical_section::with(|cs| {
271                let mut queue = self.queue.borrow(cs).borrow_mut();
272
273                if queue.schedule_wake(at, waker) {
274                    let alarm = &self.alarms.borrow(cs);
275                    let now = self.now();
276                    loop {
277                        let timestamp = queue.next_expiration(now) as super::TickType; //get next timestamp
278                        alarm.timestamp.set(timestamp); //set next alarm
279                        if timestamp > now as super::TickType {
280                            break;
281                        }
282                    }
283                }
284            })
285        }
286    }
287}