Skip to main content

esp_idf_hal/
delay.rs

1//! Delay providers.
2//!
3//! If you don't know how large your delays will be, you'll probably want to
4//! use [`Delay`]. Otherwise use [`Ets`] for delays <10ms and
5//! [`FreeRtos`] for delays >=10ms.
6//!
7//! Example of an Ets vs. FreeRtos auto-selecting delay:
8//! ```
9//!     use esp_idf_hal::delay::Delay;
10//!
11//!     let delay: Delay = Default::default();
12//!     // ...
13//!     delay.delay_us(42);
14//!     // ...
15//!     delay.delay_ms(142);
16//!     // ...
17//! ```
18//!
19//! Example of a small microsecond delay:
20//! ```
21//!     use esp_idf_hal::delay::Ets;
22//!
23//!     Ets::delay_us(42);
24//! ```
25//!
26//! Example of a millisecond delay:
27//! ```
28//!     use esp_idf_hal::delay::FreeRtos;
29//!
30//!     FreeRtos::delay_ms(42);
31//! ```
32//!
33//! Example of an [embedded_hal::delay::DelayNs] consumer with an
34//! Ets vs. FreeRtos auto-selecting delay:
35//! ```ignore
36//!     use esp_idf_hal::delay::Delay;
37//!
38//!     let mut delay: Delay = Default::default();
39//!     some_trait_user(&mut delay);
40//! ```
41
42use core::time::Duration;
43
44use esp_idf_sys::*;
45
46/// The raw OS tick type. Also see [TickType].
47pub use esp_idf_sys::TickType_t;
48
49/// Sentinel value used as "maximum delay" or "maximum blocking" marker.
50///
51/// This value is also used for representing `Option<Duration>` being `None`
52/// when converting to/from [TickType].
53pub const BLOCK: TickType_t = TickType_t::MAX;
54
55/// Sentinel value used as "no delay" or "no blocking" marker.
56pub const NON_BLOCK: TickType_t = 0;
57
58/// The configured OS tick rate in Hz.
59/// There are [TICK_RATE_HZ] number of [TickType] ticks per second.
60pub const TICK_RATE_HZ: u32 = configTICK_RATE_HZ;
61
62const MS_PER_S: u64 = 1_000;
63const NS_PER_MS: u64 = 1_000_000;
64const US_PER_MS: u32 = 1_000;
65const NS_PER_US: u32 = 1_000;
66
67#[inline]
68const fn const_min_u64(a: u64, b: u64) -> u64 {
69    if a < b {
70        a
71    } else {
72        b
73    }
74}
75
76/// Transparent wrapper around [TickType_t] with conversion methods.
77#[repr(transparent)]
78pub struct TickType(pub TickType_t);
79
80impl TickType {
81    /// Construct a [TickType] from a number of ticks.
82    #[inline]
83    pub const fn new(ticks: TickType_t) -> Self {
84        Self(ticks)
85    }
86
87    /// Construct a [TickType] from a number of milliseconds.
88    /// This function will round the number of ticks up, if required.
89    pub const fn new_millis(ms: u64) -> Self {
90        let ticks = ms
91            .saturating_mul(TICK_RATE_HZ as u64)
92            .saturating_add(MS_PER_S - 1)
93            / MS_PER_S;
94        Self(const_min_u64(ticks, TickType_t::MAX as _) as _)
95    }
96
97    /// Get the number of ticks.
98    #[inline]
99    pub const fn ticks(&self) -> TickType_t {
100        self.0
101    }
102
103    /// Convert the number of ticks to a number of milliseconds.
104    /// This function will round the number of milliseconds up, if required.
105    pub const fn as_millis(&self) -> u64 {
106        (self.0 as u64)
107            .saturating_mul(MS_PER_S)
108            .saturating_add(TICK_RATE_HZ as u64 - 1)
109            / TICK_RATE_HZ as u64
110    }
111
112    /// Convert the number of ticks to a number of milliseconds
113    /// and saturate to u32.
114    /// This function will round the number of milliseconds up, if required.
115    #[inline]
116    pub const fn as_millis_u32(&self) -> u32 {
117        const_min_u64(self.as_millis(), u32::MAX as _) as _
118    }
119}
120
121impl From<TickType_t> for TickType {
122    #[inline]
123    fn from(value: TickType_t) -> Self {
124        Self::new(value)
125    }
126}
127
128impl From<TickType> for TickType_t {
129    #[inline]
130    fn from(value: TickType) -> Self {
131        value.ticks()
132    }
133}
134
135impl From<Duration> for TickType {
136    fn from(duration: Duration) -> Self {
137        let sec_ms = duration.as_secs().saturating_mul(MS_PER_S);
138        let subsec_ns: u64 = duration.subsec_nanos().into();
139        // Convert to ms and round up. Not saturating. Cannot overflow.
140        let subsec_ms = subsec_ns.div_ceil(NS_PER_MS);
141
142        TickType::new_millis(sec_ms.saturating_add(subsec_ms))
143    }
144}
145
146impl From<Option<Duration>> for TickType {
147    fn from(duration: Option<Duration>) -> Self {
148        if let Some(duration) = duration {
149            duration.into()
150        } else {
151            TickType(BLOCK)
152        }
153    }
154}
155
156impl From<TickType> for Duration {
157    fn from(ticks: TickType) -> Self {
158        Duration::from_millis(ticks.as_millis())
159    }
160}
161
162impl From<TickType> for Option<Duration> {
163    fn from(ticks: TickType) -> Self {
164        if ticks.0 == BLOCK {
165            None
166        } else {
167            Some(ticks.into())
168        }
169    }
170}
171
172/// Espressif's built-in delay provider for small delays
173///
174/// Use only for very small delays or else the FreeRTOS IDLE tasks might starve and
175/// the IDLE task's watchdog will trigger.
176///
177/// Small delays are up to `1000 /` [TICK_RATE_HZ] milliseconds, which is typically
178/// 10 milliseconds.
179pub struct Ets;
180
181// This binding is no longer available in the generated bindings for ESP-IDF 5 or later.
182// The function itself is still available. Therefore, we define the binding here.
183#[cfg(not(esp_idf_version_major = "4"))]
184extern "C" {
185    fn ets_delay_us(us: u32);
186}
187
188impl Ets {
189    /// Pauses execution for at minimum `us` microseconds.
190    /// The delay can be longer due to rounding and/or runtime effects.
191    #[inline]
192    pub fn delay_us(us: u32) {
193        unsafe {
194            ets_delay_us(us);
195        }
196    }
197
198    /// Pauses execution for at minimum `ms` milliseconds.
199    /// The delay can be longer due to rounding and/or runtime effects.
200    /// This delay should only be used up to `1000 /` [TICK_RATE_HZ] milliseconds.
201    pub fn delay_ms(ms: u32) {
202        Self::delay_us(ms.saturating_mul(US_PER_MS));
203    }
204}
205
206impl embedded_hal_0_2::blocking::delay::DelayUs<u32> for Ets {
207    #[inline]
208    fn delay_us(&mut self, us: u32) {
209        Ets::delay_us(us);
210    }
211}
212
213impl embedded_hal_0_2::blocking::delay::DelayUs<u16> for Ets {
214    #[inline]
215    fn delay_us(&mut self, us: u16) {
216        Ets::delay_us(us.into());
217    }
218}
219
220impl embedded_hal_0_2::blocking::delay::DelayUs<u8> for Ets {
221    #[inline]
222    fn delay_us(&mut self, us: u8) {
223        Ets::delay_us(us.into());
224    }
225}
226
227impl embedded_hal_0_2::blocking::delay::DelayMs<u32> for Ets {
228    #[inline]
229    fn delay_ms(&mut self, ms: u32) {
230        Ets::delay_ms(ms);
231    }
232}
233
234impl embedded_hal_0_2::blocking::delay::DelayMs<u16> for Ets {
235    #[inline]
236    fn delay_ms(&mut self, ms: u16) {
237        Ets::delay_ms(ms.into());
238    }
239}
240
241impl embedded_hal_0_2::blocking::delay::DelayMs<u8> for Ets {
242    #[inline]
243    fn delay_ms(&mut self, ms: u8) {
244        Ets::delay_ms(ms.into());
245    }
246}
247
248impl embedded_hal::delay::DelayNs for Ets {
249    #[inline]
250    fn delay_ns(&mut self, ns: u32) {
251        Ets::delay_us(ns.saturating_add(NS_PER_US - 1) / NS_PER_US);
252    }
253
254    #[inline]
255    fn delay_us(&mut self, us: u32) {
256        Ets::delay_us(us);
257    }
258
259    #[inline]
260    fn delay_ms(&mut self, ms: u32) {
261        Ets::delay_ms(ms);
262    }
263}
264
265/// FreeRTOS-based delay provider for delays larger than 10 ms.
266///
267/// Delays bigger than `1000 /` [TICK_RATE_HZ] milliseconds (typically 10 ms) used in a
268/// loop would starve the FreeRTOS IDLE tasks as they are low prio tasks and hence the
269/// IDLE task's watchdog could trigger.
270/// This delayer avoids that by yielding to the OS during the delay.
271pub struct FreeRtos;
272
273impl FreeRtos {
274    /// Pauses execution for at minimum `ms` milliseconds.
275    /// The delay can be longer due to rounding and/or runtime effects.
276    pub fn delay_ms(ms: u32) {
277        let ticks = TickType::new_millis(ms.into()).ticks();
278        unsafe {
279            vTaskDelay(ticks);
280        }
281    }
282
283    // Internal helper: Round up to ms.
284    // This is not supposed to be `pub`, because the user code shall not use this
285    // timer for microsecond delay. Only used for trait impl below.
286    fn delay_us(us: u32) {
287        Self::delay_ms(us.saturating_add(US_PER_MS - 1) / US_PER_MS);
288    }
289}
290
291impl embedded_hal_0_2::blocking::delay::DelayUs<u32> for FreeRtos {
292    #[inline]
293    fn delay_us(&mut self, us: u32) {
294        FreeRtos::delay_us(us);
295    }
296}
297
298impl embedded_hal_0_2::blocking::delay::DelayUs<u16> for FreeRtos {
299    #[inline]
300    fn delay_us(&mut self, us: u16) {
301        FreeRtos::delay_us(us.into());
302    }
303}
304
305impl embedded_hal_0_2::blocking::delay::DelayUs<u8> for FreeRtos {
306    #[inline]
307    fn delay_us(&mut self, us: u8) {
308        FreeRtos::delay_us(us.into());
309    }
310}
311
312impl embedded_hal_0_2::blocking::delay::DelayMs<u32> for FreeRtos {
313    #[inline]
314    fn delay_ms(&mut self, ms: u32) {
315        FreeRtos::delay_ms(ms);
316    }
317}
318
319impl embedded_hal_0_2::blocking::delay::DelayMs<u16> for FreeRtos {
320    #[inline]
321    fn delay_ms(&mut self, ms: u16) {
322        FreeRtos::delay_ms(ms.into());
323    }
324}
325
326impl embedded_hal_0_2::blocking::delay::DelayMs<u8> for FreeRtos {
327    #[inline]
328    fn delay_ms(&mut self, ms: u8) {
329        FreeRtos::delay_ms(ms.into());
330    }
331}
332
333impl embedded_hal::delay::DelayNs for FreeRtos {
334    #[inline]
335    fn delay_ns(&mut self, ns: u32) {
336        FreeRtos::delay_us(ns.saturating_add(NS_PER_US - 1) / NS_PER_US);
337    }
338
339    #[inline]
340    fn delay_us(&mut self, us: u32) {
341        FreeRtos::delay_us(us);
342    }
343
344    #[inline]
345    fn delay_ms(&mut self, ms: u32) {
346        FreeRtos::delay_ms(ms);
347    }
348}
349
350/// A delay provider that uses [`Ets`] for delays below a certain threshold
351/// and [`FreeRtos`] for delays equal or above the threshold.
352#[derive(Copy, Clone)]
353pub struct Delay(u32);
354
355impl Delay {
356    /// Create a [Delay] with a default threshold of 1 ms.
357    #[inline]
358    pub const fn new_default() -> Self {
359        Self::new(1000)
360    }
361
362    /// Create a [Delay] with a threshold of the specified amount of microseconds.
363    #[inline]
364    pub const fn new(threshold_us: u32) -> Self {
365        Self(threshold_us)
366    }
367
368    /// Pauses execution for at minimum `us` microseconds.
369    /// The delay can be longer due to rounding and/or runtime effects.
370    #[inline]
371    pub fn delay_us(&self, us: u32) {
372        if us < self.0 {
373            Ets::delay_us(us);
374        } else {
375            FreeRtos::delay_us(us);
376        }
377    }
378
379    /// Pauses execution for at minimum `ms` milliseconds.
380    /// The delay can be longer due to rounding and/or runtime effects.
381    pub fn delay_ms(&self, ms: u32) {
382        if ms.saturating_mul(US_PER_MS) < self.0 {
383            Ets::delay_ms(ms);
384        } else {
385            FreeRtos::delay_ms(ms);
386        }
387    }
388}
389
390impl Default for Delay {
391    #[inline]
392    fn default() -> Self {
393        Self::new_default()
394    }
395}
396
397impl embedded_hal::delay::DelayNs for Delay {
398    #[inline]
399    fn delay_ns(&mut self, ns: u32) {
400        Delay::delay_us(self, ns.saturating_add(NS_PER_US - 1) / NS_PER_US)
401    }
402
403    #[inline]
404    fn delay_us(&mut self, us: u32) {
405        Delay::delay_us(self, us)
406    }
407
408    #[inline]
409    fn delay_ms(&mut self, ms: u32) {
410        Delay::delay_ms(self, ms)
411    }
412}
413
414impl embedded_hal_0_2::blocking::delay::DelayUs<u8> for Delay {
415    #[inline]
416    fn delay_us(&mut self, us: u8) {
417        Delay::delay_us(self, us.into());
418    }
419}
420
421impl embedded_hal_0_2::blocking::delay::DelayUs<u16> for Delay {
422    #[inline]
423    fn delay_us(&mut self, us: u16) {
424        Delay::delay_us(self, us.into());
425    }
426}
427
428impl embedded_hal_0_2::blocking::delay::DelayUs<u32> for Delay {
429    #[inline]
430    fn delay_us(&mut self, us: u32) {
431        Delay::delay_us(self, us);
432    }
433}
434
435impl embedded_hal_0_2::blocking::delay::DelayMs<u8> for Delay {
436    #[inline]
437    fn delay_ms(&mut self, ms: u8) {
438        Delay::delay_ms(self, ms.into())
439    }
440}
441
442impl embedded_hal_0_2::blocking::delay::DelayMs<u16> for Delay {
443    #[inline]
444    fn delay_ms(&mut self, ms: u16) {
445        Delay::delay_ms(self, ms.into())
446    }
447}
448
449impl embedded_hal_0_2::blocking::delay::DelayMs<u32> for Delay {
450    #[inline]
451    fn delay_ms(&mut self, ms: u32) {
452        Delay::delay_ms(self, ms)
453    }
454}