Skip to main content

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