Skip to main content

esp_idf_svc/
timer.rs

1//! High resolution hardware timer based task scheduling
2//!
3//! Although FreeRTOS provides software timers, these timers have a few
4//! limitations:
5//!
6//! - Maximum resolution is equal to RTOS tick period
7//! - Timer callbacks are dispatched from a low-priority task
8//!
9//! EspTimer is a set of APIs that provides one-shot and periodic timers,
10//! microsecond time resolution, and 52-bit range.
11
12use core::num::NonZeroU32;
13use core::time::Duration;
14use core::{ffi, ptr};
15
16extern crate alloc;
17use alloc::boxed::Box;
18use alloc::sync::Arc;
19
20use esp_idf_hal::task::asynch::Notification;
21
22use crate::sys::*;
23
24use ::log::debug;
25
26#[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)]
27pub use isr::*;
28
29use crate::handle::RawHandle;
30
31struct UnsafeCallback<'a>(*mut Box<dyn FnMut() + Send + 'a>);
32
33impl<'a> UnsafeCallback<'a> {
34    fn from(boxed: &mut Box<dyn FnMut() + Send + 'a>) -> Self {
35        Self(boxed)
36    }
37
38    unsafe fn from_ptr(ptr: *mut ffi::c_void) -> Self {
39        Self(ptr as *mut _)
40    }
41
42    fn as_ptr(&self) -> *mut ffi::c_void {
43        self.0 as *mut _
44    }
45
46    unsafe fn call(&self) {
47        let reference = self.0.as_mut().unwrap();
48
49        (reference)();
50    }
51}
52
53pub struct EspTimer<'a> {
54    handle: esp_timer_handle_t,
55    _callback: Box<dyn FnMut() + Send + 'a>,
56}
57
58impl EspTimer<'_> {
59    pub fn is_scheduled(&self) -> Result<bool, EspError> {
60        Ok(unsafe { esp_timer_is_active(self.handle) })
61    }
62
63    pub fn cancel(&self) -> Result<bool, EspError> {
64        let res = unsafe { esp_timer_stop(self.handle) };
65
66        Ok(res != ESP_OK)
67    }
68
69    pub fn after(&self, duration: Duration) -> Result<(), EspError> {
70        self.cancel()?;
71
72        esp!(unsafe { esp_timer_start_once(self.handle, duration.as_micros() as _) })?;
73
74        Ok(())
75    }
76
77    pub fn every(&self, duration: Duration) -> Result<(), EspError> {
78        self.cancel()?;
79
80        esp!(unsafe { esp_timer_start_periodic(self.handle, duration.as_micros() as _) })?;
81
82        Ok(())
83    }
84
85    extern "C" fn handle(arg: *mut ffi::c_void) {
86        if crate::hal::interrupt::active() {
87            #[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)]
88            {
89                let signaled = crate::hal::interrupt::with_isr_yield_signal(move || unsafe {
90                    UnsafeCallback::from_ptr(arg).call();
91                });
92
93                if signaled {
94                    unsafe {
95                        crate::sys::esp_timer_isr_dispatch_need_yield();
96                    }
97                }
98            }
99
100            #[cfg(not(esp_idf_esp_timer_supports_isr_dispatch_method))]
101            {
102                unreachable!();
103            }
104        } else {
105            unsafe {
106                UnsafeCallback::from_ptr(arg).call();
107            }
108        }
109    }
110}
111
112unsafe impl Send for EspTimer<'_> {}
113
114impl Drop for EspTimer<'_> {
115    fn drop(&mut self) {
116        self.cancel().unwrap();
117
118        while unsafe { esp_timer_delete(self.handle) } != ESP_OK {
119            // Timer is still running, busy-loop
120        }
121
122        debug!("Timer dropped");
123    }
124}
125
126impl RawHandle for EspTimer<'_> {
127    type Handle = esp_timer_handle_t;
128
129    fn handle(&self) -> Self::Handle {
130        self.handle
131    }
132}
133
134pub struct EspAsyncTimer {
135    timer: EspTimer<'static>,
136    notification: Arc<Notification>,
137}
138
139impl EspAsyncTimer {
140    pub async fn after(&mut self, duration: Duration) -> Result<(), EspError> {
141        self.timer.cancel()?;
142
143        self.notification.reset();
144        self.timer.after(duration)?;
145
146        self.notification.wait().await;
147
148        Ok(())
149    }
150
151    pub fn every(&mut self, duration: Duration) -> Result<&'_ mut Self, EspError> {
152        self.timer.cancel()?;
153
154        self.notification.reset();
155        self.timer.every(duration)?;
156
157        Ok(self)
158    }
159
160    pub async fn tick(&mut self) -> Result<(), EspError> {
161        self.notification.wait().await;
162
163        Ok(())
164    }
165}
166
167impl embedded_hal_async::delay::DelayNs for EspAsyncTimer {
168    async fn delay_ns(&mut self, ns: u32) {
169        EspAsyncTimer::after(self, Duration::from_micros(ns as _))
170            .await
171            .unwrap();
172    }
173
174    async fn delay_ms(&mut self, ms: u32) {
175        EspAsyncTimer::after(self, Duration::from_millis(ms as _))
176            .await
177            .unwrap();
178    }
179}
180
181pub trait EspTimerServiceType {
182    fn is_isr() -> bool;
183}
184
185#[derive(Clone, Debug)]
186pub struct Task;
187
188impl EspTimerServiceType for Task {
189    fn is_isr() -> bool {
190        false
191    }
192}
193
194pub struct EspTimerService<T>(T)
195where
196    T: EspTimerServiceType;
197
198impl<T> EspTimerService<T>
199where
200    T: EspTimerServiceType,
201{
202    pub fn now(&self) -> Duration {
203        Duration::from_micros(unsafe { esp_timer_get_time() as _ })
204    }
205
206    pub fn timer<F>(&self, callback: F) -> Result<EspTimer<'static>, EspError>
207    where
208        F: FnMut() + Send + 'static,
209    {
210        self.internal_timer(callback, false)
211    }
212
213    /// Same as `timer` but does not wake the device from light sleep.
214    pub fn timer_nowake<F>(&self, callback: F) -> Result<EspTimer<'static>, EspError>
215    where
216        F: FnMut() + Send + 'static,
217    {
218        self.internal_timer(callback, true)
219    }
220
221    pub fn timer_async(&self) -> Result<EspAsyncTimer, EspError> {
222        self.internal_timer_async(false)
223    }
224
225    /// Same as `timer_async` but does not wake the device from light sleep.
226    pub fn timer_async_nowake(&self) -> Result<EspAsyncTimer, EspError> {
227        self.internal_timer_async(true)
228    }
229
230    /// # Safety
231    ///
232    /// This method - in contrast to method `timer` - allows the user to pass
233    /// a non-static callback/closure. This enables users to borrow
234    /// - in the closure - variables that live on the stack - or more generally - in the same
235    ///   scope where the service is created.
236    ///
237    /// HOWEVER: care should be taken NOT to call `core::mem::forget()` on the service,
238    /// as that would immediately lead to an UB (crash).
239    /// Also note that forgetting the service might happen with `Rc` and `Arc`
240    /// when circular references are introduced: <https://github.com/rust-lang/rust/issues/24456>
241    ///
242    /// The reason is that the closure is actually sent to a hidden ESP IDF thread.
243    /// This means that if the service is forgotten, Rust is free to e.g. unwind the stack
244    /// and the closure now owned by this other thread will end up with references to variables that no longer exist.
245    ///
246    /// The destructor of the service takes care - prior to the service being dropped and e.g.
247    /// the stack being unwind - to remove the closure from the hidden thread and destroy it.
248    /// Unfortunately, when the service is forgotten, the un-subscription does not happen
249    /// and invalid references are left dangling.
250    ///
251    /// This "local borrowing" will only be possible to express in a safe way once/if `!Leak` types
252    /// are introduced to Rust (i.e. the impossibility to "forget" a type and thus not call its destructor).
253    pub unsafe fn timer_nonstatic<'a, F>(&self, callback: F) -> Result<EspTimer<'a>, EspError>
254    where
255        F: FnMut() + Send + 'a,
256    {
257        self.internal_timer(callback, false)
258    }
259
260    /// # Safety
261    ///
262    /// Same as `timer_nonstatic` but does not wake the device from light sleep.
263    pub unsafe fn timer_nonstatic_nowake<'a, F>(
264        &self,
265        callback: F,
266    ) -> Result<EspTimer<'a>, EspError>
267    where
268        F: FnMut() + Send + 'a,
269    {
270        self.internal_timer(callback, true)
271    }
272
273    fn internal_timer<'a, F>(
274        &self,
275        callback: F,
276        skip_unhandled_events: bool,
277    ) -> Result<EspTimer<'a>, EspError>
278    where
279        F: FnMut() + Send + 'a,
280    {
281        let mut handle: esp_timer_handle_t = ptr::null_mut();
282
283        let boxed_callback: Box<dyn FnMut() + Send + 'a> = Box::new(callback);
284
285        let mut callback = Box::new(boxed_callback);
286        let unsafe_callback = UnsafeCallback::from(&mut callback);
287
288        #[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)]
289        let dispatch_method = if T::is_isr() {
290            esp_timer_dispatch_t_ESP_TIMER_ISR
291        } else {
292            esp_timer_dispatch_t_ESP_TIMER_TASK
293        };
294
295        #[cfg(not(esp_idf_esp_timer_supports_isr_dispatch_method))]
296        let dispatch_method = esp_timer_dispatch_t_ESP_TIMER_TASK;
297
298        esp!(unsafe {
299            esp_timer_create(
300                &esp_timer_create_args_t {
301                    callback: Some(EspTimer::handle),
302                    name: b"rust\0" as *const _ as *const _, // TODO
303                    arg: unsafe_callback.as_ptr(),
304                    dispatch_method,
305                    skip_unhandled_events,
306                },
307                &mut handle as *mut _,
308            )
309        })?;
310
311        Ok(EspTimer {
312            handle,
313            _callback: callback,
314        })
315    }
316
317    fn internal_timer_async(&self, skip_unhandled_events: bool) -> Result<EspAsyncTimer, EspError> {
318        let notification = Arc::new(Notification::new());
319
320        let timer = {
321            let notification = Arc::downgrade(&notification);
322
323            self.internal_timer(
324                move || {
325                    if let Some(notification) = notification.upgrade() {
326                        notification.notify(NonZeroU32::new(1).unwrap());
327                    }
328                },
329                skip_unhandled_events,
330            )?
331        };
332
333        Ok(EspAsyncTimer {
334            timer,
335            notification,
336        })
337    }
338}
339
340pub type EspTaskTimerService = EspTimerService<Task>;
341
342impl EspTimerService<Task> {
343    pub fn new() -> Result<Self, EspError> {
344        Ok(Self(Task))
345    }
346}
347
348impl<T> Clone for EspTimerService<T>
349where
350    T: EspTimerServiceType + Clone,
351{
352    fn clone(&self) -> Self {
353        Self(self.0.clone())
354    }
355}
356
357#[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)]
358mod isr {
359    use crate::sys::EspError;
360
361    #[derive(Clone, Debug)]
362    pub struct ISR;
363
364    impl super::EspTimerServiceType for ISR {
365        fn is_isr() -> bool {
366            true
367        }
368    }
369
370    pub type EspISRTimerService = super::EspTimerService<ISR>;
371
372    impl EspISRTimerService {
373        /// # Safety
374        /// TODO
375        pub unsafe fn new() -> Result<Self, EspError> {
376            Ok(Self(ISR))
377        }
378    }
379}
380
381/// This module is used to provide a time driver for the `embassy-time` crate.
382///
383/// The minimum provided resolution is ~ 20-30us when the CPU is at top speed of 240MHz
384/// (https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32/api-reference/system/esp_timer.html#timeout-value-limits)
385///
386/// The tick-rate is 1MHz (i.e. 1 tick is 1us).
387#[cfg(feature = "embassy-time-driver")]
388pub mod embassy_time_driver {
389    use core::cell::RefCell;
390    use core::task::Waker;
391
392    use ::embassy_time_driver::Driver;
393    use embassy_time_queue_utils::Queue;
394
395    use crate::private::mutex::Mutex;
396    use crate::timer::*;
397
398    struct EspDriverInner {
399        queue: embassy_time_queue_utils::Queue,
400        timer: Option<EspTimer<'static>>,
401    }
402
403    impl EspDriverInner {
404        fn now() -> u64 {
405            unsafe { esp_timer_get_time() as _ }
406        }
407
408        fn schedule_next_expiration(&mut self) {
409            /// End of epoch minus one day
410            const MAX_SAFE_TIMEOUT_US: u64 = u64::MAX - 24 * 60 * 60 * 1000 * 1000;
411
412            let timer = self.timer.as_mut().unwrap();
413
414            loop {
415                let now = Self::now();
416                let next_at = self.queue.next_expiration(now);
417
418                if now < next_at {
419                    let after = next_at - now;
420
421                    if after <= MAX_SAFE_TIMEOUT_US {
422                        // Why?
423                        // The ESP-IDF Timer API does not have a `Timer::at` method so we have to call it with
424                        // `Timer::after(next_at - now)` instead. The problem is - even though the ESP IDF
425                        // Timer API does not have a `Timer::at` method - _internally_ it takes our `next_at - now`,
426                        // adds to it a **newer** "now" and sets this as the moment in time when the timer should trigger.
427                        //
428                        // Consider what would happen if we call `Timer::after(u64::MAX - now)`:
429                        // The result would be something like `u64::MAX - now + (now + 1)` which would silently overflow and
430                        // trigger the timer after 1us:
431                        // https://github.com/espressif/esp-idf/blob/b5ac4fbdf9e9fb320bb0a98ee4fbaa18f8566f37/components/esp_timer/src/esp_timer.c#L188
432                        //
433                        // To workaround this problem, we make sure to never call `Timer::after(ms)` with `ms` greater than `MAX_SAFE_TIMEOUT_US`
434                        // (i.e. the end of epoch - one day).
435                        //
436                        // Thus, even if we are un-scheduled between the calculation of our own `now` and the driver's newer `now`,
437                        // there is one extra **day** of millis to accomodate for the potential overflow. If the overflow does happen still
438                        // (which is kinda unthinkable given the time scales we are working with), the timer will re-trigger immediately,
439                        // but hopefully on the next (or next after next and so on) re-trigger, we won't have the overflow anymore.
440                        timer.after(Duration::from_micros(after)).unwrap();
441                    }
442
443                    break;
444                }
445            }
446        }
447    }
448
449    struct EspDriver {
450        inner: Mutex<RefCell<EspDriverInner>>,
451    }
452
453    impl EspDriver {
454        const fn new() -> Self {
455            Self {
456                inner: Mutex::new(RefCell::new(EspDriverInner {
457                    queue: Queue::new(),
458                    timer: None,
459                })),
460            }
461        }
462    }
463
464    unsafe impl Send for EspDriver {}
465    unsafe impl Sync for EspDriver {}
466
467    impl Driver for EspDriver {
468        fn now(&self) -> u64 {
469            EspDriverInner::now()
470        }
471
472        fn schedule_wake(&self, at: u64, waker: &Waker) {
473            let service = EspTimerService::<Task>::new().unwrap();
474
475            let guard = self.inner.lock();
476            let mut inner = guard.borrow_mut();
477
478            if inner.timer.is_none() {
479                // Driver is always statically allocated, so this is safe
480                let static_self: &'static Self = unsafe { core::mem::transmute(self) };
481
482                inner.timer = Some(
483                    service
484                        .timer(move || {
485                            static_self
486                                .inner
487                                .lock()
488                                .borrow_mut()
489                                .schedule_next_expiration()
490                        })
491                        .unwrap(),
492                );
493            }
494
495            if inner.queue.schedule_wake(at, waker) {
496                inner.schedule_next_expiration();
497            }
498        }
499    }
500
501    pub type LinkWorkaround = [*mut (); 2];
502
503    #[used]
504    static mut __INTERNAL_REFERENCE: LinkWorkaround = [
505        _embassy_time_now as *mut _,
506        _embassy_time_schedule_wake as *mut _,
507    ];
508
509    pub fn link() -> LinkWorkaround {
510        unsafe { __INTERNAL_REFERENCE }
511    }
512
513    ::embassy_time_driver::time_driver_impl!(static DRIVER: EspDriver = EspDriver::new());
514}