esp_idf_hal/timer.rs
1//! Timer driver for the [Timer Group peripheral].
2//!
3//! This timer can select different clock sources and prescalers to meet the requirements
4//! of nanosecond-level resolution. Additionally, it has flexible timeout alarm functions
5//! and allows automatic updating of the count value at the alarm moment,
6//! achieving very precise timing cycles.
7//!
8//! Based on the high resolution, high count range, and high response capabilities of the
9//! hardware timer, the main application scenarios of this driver include:
10//! - Running freely as a calendar clock to provide timestamp services for other modules
11//! - Generating periodic alarms to complete periodic tasks
12//! - Generating one-shot alarms, which can be used to implement a monotonic software timer list
13//! with asynchronous updates of alarm values
14//! - Working with the GPIO module to achieve PWM signal output and input capture
15//! - ...
16//!
17//! [Timer Group peripheral]: https://documentation.espressif.com/esp32_technical_reference_manual_en.pdf#timg
18//!
19//! # Driver redesign in ESP-IDF 5.0
20//!
21//! In ESP-IDF 5.0, the [timer API was redesigned] to simplify and unify the usage of
22//! general purpose timer.
23//!
24//! It is recommended to use the new API, but for now the old API is available through
25//! the `timer-legacy` feature. The ESP-IDF 6.0 release will remove support for the legacy API.
26//!
27//! [timer API was redesigned]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/migration-guides/release-5.x/5.0/peripherals.html#timer-group-driver
28//!
29//! # Force enabling debug logs
30//!
31//! The `CONFIG_GPTIMER_ENABLE_DEBUG_LOG` option in the `sdkconfig` forces the
32//! GPTimer driver to enable all debug logs, regardless of the global log level settings.
33//! Enabling this option helps developers obtain more detailed log information during
34//! debugging, making it easier to locate and solve problems.
35use core::ffi::c_void;
36use core::time::Duration;
37use core::{fmt, ptr};
38
39use alloc::boxed::Box;
40
41use esp_idf_sys::*;
42
43use crate::interrupt;
44use crate::interrupt::asynch::HalIsrNotification;
45use crate::units::{FromValueType, Hertz};
46use config::*;
47
48/// This might not always be available in the generated `esp-idf-sys` bindings,
49/// which is why it is defined here.
50pub const ERR_EOVERFLOW: esp_err_t = 139;
51
52/// GPTimer alarm event data.
53#[derive(Debug, Clone, PartialEq, Eq)]
54#[non_exhaustive]
55pub struct AlarmEventData {
56 /// Current count value
57 pub count_value: u64,
58 /// Current alarm value
59 pub alarm_value: u64,
60}
61
62impl From<gptimer_alarm_event_data_t> for AlarmEventData {
63 fn from(value: gptimer_alarm_event_data_t) -> Self {
64 Self {
65 count_value: value.count_value,
66 alarm_value: value.alarm_value,
67 }
68 }
69}
70
71/// Timer configuration
72pub mod config {
73 use esp_idf_sys::*;
74
75 use crate::units::{FromValueType, Hertz};
76
77 /// GPTimer count direction
78 #[derive(Debug, Clone, Default)]
79 #[non_exhaustive]
80 pub enum CountDirection {
81 /// Decrease count value
82 #[default]
83 Up,
84 /// Increase count value
85 Down,
86 }
87
88 impl From<CountDirection> for gptimer_count_direction_t {
89 fn from(value: CountDirection) -> Self {
90 match value {
91 CountDirection::Up => gptimer_count_direction_t_GPTIMER_COUNT_UP,
92 CountDirection::Down => gptimer_count_direction_t_GPTIMER_COUNT_DOWN,
93 }
94 }
95 }
96
97 /// Type of GPTimer clock source.
98 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
99 pub enum ClockSource {
100 /// Use the default clock source.
101 #[default]
102 Default,
103 /// Select `APB` as the source clock
104 #[cfg(any(esp32, esp32s2, esp32s3, esp32c3))]
105 APB,
106 /// Select `RC_FAST` as the source clock
107 #[cfg(any(esp32c5, esp32c6, esp32c61, esp32h2, esp32p4))]
108 RcFast,
109 /// Select `XTAL` as the source clock
110 #[cfg(any(
111 esp32s2, esp32s3, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h2, esp32p4
112 ))]
113 XTAL,
114 /// Select `PLL_F40M` as the source clock
115 #[cfg(esp32c2)]
116 PLLF40M,
117 /// Select `PLL_F48M` as the source clock
118 #[cfg(esp32h2)]
119 PLLF48M,
120 /// Select `PLL_F80M` as the source clock
121 #[cfg(any(esp32c5, esp32c6, esp32c61, esp32p4))]
122 PLLF80M,
123 }
124
125 impl From<ClockSource> for soc_periph_gptimer_clk_src_t {
126 fn from(clock: ClockSource) -> Self {
127 match clock {
128 ClockSource::Default => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_DEFAULT,
129 #[cfg(any(esp32, esp32s2, esp32s3, esp32c3))]
130 ClockSource::APB => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_APB,
131 #[cfg(any(esp32c5, esp32c6, esp32c61, esp32h2, esp32p4))]
132 ClockSource::RcFast => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_RC_FAST,
133 #[cfg(any(
134 esp32s2, esp32s3, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h2,
135 esp32p4
136 ))]
137 ClockSource::XTAL => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_XTAL,
138 #[cfg(esp32c2)]
139 ClockSource::PLLF40M => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_PLL_F40M,
140 #[cfg(esp32h2)]
141 ClockSource::PLLF48M => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_PLL_F48M,
142 #[cfg(any(esp32c5, esp32c6, esp32c61, esp32p4))]
143 ClockSource::PLLF80M => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_PLL_F80M,
144 }
145 }
146 }
147
148 /// Configuration for [`TimerDriver`](super::TimerDriver)
149 #[derive(Debug, Clone)]
150 pub struct TimerConfig {
151 /// GPTimer clock source
152 pub clock_source: ClockSource,
153 /// Count direction
154 pub direction: CountDirection,
155 /// Counter resolution (working frequency) in Hz, hence,
156 /// the step size of each count tick equals to (1 / resolution_hz) seconds
157 pub resolution: Hertz,
158 /// GPTimer interrupt priority, if set to 0, the driver will try to allocate an interrupt
159 /// with a relative low priority (1,2,3)
160 #[cfg(esp_idf_version_at_least_5_1_2)]
161 #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_2)))]
162 pub intr_priority: i32,
163 /// If set true, the timer interrupt number can be shared with other peripherals
164 pub intr_shared: bool,
165 /// If set, driver allows the power domain to be powered off when system enters
166 /// sleep mode. This can save power, but at the expense of more RAM being consumed
167 /// to save register context.
168 #[cfg(esp_idf_version_at_least_5_4_0)]
169 #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_4_0)))]
170 pub allow_pd: bool,
171 // This field is intentionally hidden to prevent non-exhaustive pattern matching.
172 // You should only construct this struct using the `..Default::default()` pattern.
173 // If you use this field directly, your code might break in future versions.
174 #[doc(hidden)]
175 #[allow(dead_code)]
176 pub __internal: (),
177 }
178
179 impl Default for TimerConfig {
180 fn default() -> Self {
181 Self {
182 clock_source: Default::default(),
183 direction: Default::default(),
184 resolution: 1_000_000.Hz(), // 1 MHz
185 #[cfg(esp_idf_version_at_least_5_1_2)]
186 intr_priority: 0,
187 intr_shared: false,
188 #[cfg(esp_idf_version_at_least_5_4_0)]
189 allow_pd: false,
190 __internal: (),
191 }
192 }
193 }
194
195 /// General Purpose Timer alarm configuration.
196 #[derive(Debug, Clone)]
197 pub struct AlarmConfig {
198 /// Alarm reload count value, effect only when [`Self::auto_reload_on_alarm`] is set to true
199 pub reload_count: u64,
200 /// Alarm target count value
201 pub alarm_count: u64,
202 /// Reload the count value by hardware, immediately at the alarm event
203 pub auto_reload_on_alarm: bool,
204 // This field is intentionally hidden to prevent non-exhaustive pattern matching.
205 // You should only construct this struct using the `..Default::default()` pattern.
206 // If you use this field directly, your code might break in future versions.
207 #[doc(hidden)]
208 #[allow(dead_code)]
209 pub __internal: (),
210 }
211
212 impl Default for AlarmConfig {
213 fn default() -> Self {
214 Self {
215 reload_count: 0,
216 alarm_count: 1_000_000,
217 auto_reload_on_alarm: false,
218 __internal: (),
219 }
220 }
221 }
222
223 impl From<&AlarmConfig> for gptimer_alarm_config_t {
224 fn from(config: &AlarmConfig) -> Self {
225 gptimer_alarm_config_t {
226 alarm_count: config.alarm_count,
227 reload_count: config.reload_count,
228 flags: gptimer_alarm_config_t__bindgen_ty_1 {
229 _bitfield_1: gptimer_alarm_config_t__bindgen_ty_1::new_bitfield_1(
230 config.auto_reload_on_alarm as _,
231 ),
232 ..Default::default()
233 },
234 }
235 }
236 }
237}
238
239struct AlarmUserData<'d> {
240 on_alarm: Box<dyn FnMut(AlarmEventData) + Send + 'd>,
241 notif: HalIsrNotification,
242}
243
244/// General Purpose Timer driver.
245///
246/// You can use this driver to get notified when a certain amount of time has passed
247/// ([`TimerDriver::subscribe`]), or to measure time intervals ([`TimerDriver::get_raw_count`]).
248///
249/// The driver has the following states:
250/// - "init" state: After creation of the driver, or after calling [`TimerDriver::disable`].
251/// - "enable" state: After calling [`TimerDriver::enable`]. In this state, the timer is ready to be started,
252/// but the internal counter is not running yet.
253/// - "run" state: After calling [`TimerDriver::start`]. In this state, the internal counter is running.
254/// To stop the counter, call [`TimerDriver::stop`], which would transition back to "enable" state.
255pub struct TimerDriver<'d> {
256 handle: gptimer_handle_t,
257 on_alarm: Option<Box<AlarmUserData<'d>>>,
258}
259
260impl<'d> TimerDriver<'d> {
261 /// Create a new General Purpose Timer, and return the handle.
262 ///
263 /// The state of the returned timer will be "init" state.
264 ///
265 /// # Errors
266 ///
267 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument
268 /// - `ESP_ERR_NO_MEM`: Failed because out of memory
269 /// - `ESP_ERR_NOT_FOUND`: Failed because all hardware timers are used up and no more free one
270 /// - `ESP_FAIL`: Failed because of other error
271 pub fn new(config: &TimerConfig) -> Result<Self, EspError> {
272 let sys_config = gptimer_config_t {
273 clk_src: config.clock_source.into(),
274 direction: config.direction.clone().into(),
275 resolution_hz: config.resolution.into(),
276 #[cfg(esp_idf_version_at_least_5_1_2)]
277 intr_priority: config.intr_priority,
278 flags: gptimer_config_t__bindgen_ty_1 {
279 _bitfield_1: gptimer_config_t__bindgen_ty_1::new_bitfield_1(
280 config.intr_shared as _,
281 #[cfg(esp_idf_version_at_least_5_4_0)]
282 {
283 config.allow_pd as _
284 },
285 // The `backup_before_sleep` field is deprecated, and will be removed in 6.1
286 #[cfg(all(
287 esp_idf_version_at_least_5_3_0,
288 not(esp_idf_version_at_least_6_1_0)
289 ))]
290 {
291 false as _
292 },
293 ),
294 ..Default::default()
295 },
296 };
297 let mut handle = ptr::null_mut();
298
299 esp!(unsafe { gptimer_new_timer(&sys_config, &raw mut handle) })?;
300
301 Ok(Self {
302 handle,
303 on_alarm: None,
304 })
305 }
306
307 /// Returns the underlying GPTimer handle.
308 pub fn handle(&self) -> gptimer_handle_t {
309 self.handle
310 }
311
312 /// Converts the given duration to the corresponding timer count value.
313 ///
314 /// # Errors
315 ///
316 /// If [`Self::get_resolution`] fails, this function will return the same error.
317 ///
318 /// This function might overflow if the duration is too long to be represented
319 /// as ticks with the current timer resolution.
320 /// In that case an error with the code [`ERR_EOVERFLOW`] will be returned.
321 #[cfg(esp_idf_version_at_least_5_1_0)]
322 #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
323 pub fn duration_to_count(&self, duration: Duration) -> Result<u64, EspError> {
324 // 1 / resolution = how many seconds per tick
325 // -> duration / (1 / resolution) = duration * resolution = how many ticks in duration (where duration is in seconds)
326 //
327 // The below calculation splits the duration into full seconds and the remainder in nanoseconds.
328 // The full seconds can simply be multiplied by the ticks per second (resolution).
329 //
330 // The remainder in nanoseconds would have to be converted to seconds to multiply with the resolution,
331 // but with whole numbers that would round down to zero (remainder is less than a full second).
332 // Therefore instead of:
333 // ticks += resolution * (duration_rem_in_nanos / 1_000_000_000)
334 // we do:
335 // ticks += (resolution * duration_rem_in_nanos) / 1_000_000_000
336 //
337 // The 1_000_000_000 is one second in nanoseconds.
338
339 let ticks_per_second = self.get_resolution()?.0 as u64;
340 let duration_in_seconds = duration.as_secs();
341 let duration_rem_in_nanos = duration.subsec_nanos() as u64;
342
343 let Some(ticks) = ticks_per_second.checked_mul(duration_in_seconds) else {
344 return Err(EspError::from_infallible::<ERR_EOVERFLOW>());
345 };
346
347 ticks
348 .checked_add(
349 (ticks_per_second * duration_rem_in_nanos)
350 / Duration::from_secs(1).as_nanos() as u64,
351 )
352 .ok_or(EspError::from_infallible::<ERR_EOVERFLOW>())
353 }
354
355 /// Asynchronously delay for the specified duration.
356 ///
357 /// This function will reset the timer count to 0, and set a one-shot alarm.
358 /// Any existing count or alarm configuration will be overwritten.
359 /// This function does **not** [`Self::start`] or [`Self::enable`] the timer,
360 /// this must be done beforehand.
361 ///
362 /// # Errors
363 ///
364 /// If there is no interrupt service registered, it will return an `ESP_ERR_INVALID_STATE`.
365 /// To enable interrupts, either register your own callback through
366 /// [`Self::subscribe`]/[`Self::subscribe_nonstatic`], or call [`Self::subscribe_default`].
367 #[cfg(esp_idf_version_at_least_5_1_0)]
368 #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
369 pub async fn delay(&self, duration: Duration) -> Result<(), EspError> {
370 let alarm_count = self.duration_to_count(duration)?;
371
372 // Set alarm and reset the current count to 0
373 self.set_raw_count(0)?;
374 self.set_alarm_action(Some(&AlarmConfig {
375 alarm_count,
376 ..Default::default()
377 }))?;
378
379 let res = self.wait().await;
380
381 // Unset the previous alarm
382 self.set_alarm_action(None)?;
383
384 res
385 }
386
387 fn notif(&self) -> Result<&HalIsrNotification, EspError> {
388 self.on_alarm
389 .as_ref()
390 .map(|data| &data.notif)
391 .ok_or(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())
392 }
393
394 /// Wait for the timer alarm event interrupt.
395 ///
396 /// # Errors
397 ///
398 /// If this function is called without a registered ISR, it will
399 /// return an `ESP_ERR_INVALID_STATE`.
400 /// To enable interrupts, either register your own callback through
401 /// [`Self::subscribe`]/[`Self::subscribe_nonstatic`], or call [`Self::subscribe_default`].
402 pub async fn wait(&self) -> Result<(), EspError> {
403 self.notif()?.wait().await;
404
405 Ok(())
406 }
407
408 /// Resets the internal wait notification.
409 ///
410 /// If no callback is registered, this function does nothing.
411 pub fn reset_wait(&self) {
412 if let Ok(notif) = self.notif() {
413 notif.reset();
414 }
415 }
416
417 /// Set GPTimer raw count value.
418 ///
419 /// When updating the raw count of an active timer, the timer will
420 /// immediately start counting from the new value.
421 ///
422 /// # Errors
423 ///
424 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument
425 /// - `ESP_FAIL`: Failed because of other error
426 pub fn set_raw_count(&self, value: u64) -> Result<(), EspError> {
427 esp!(unsafe { gptimer_set_raw_count(self.handle, value) })
428 }
429
430 /// Get GPTimer raw count value.
431 ///
432 /// This function will trigger a software capture event and then return the captured count value.
433 ///
434 /// With the raw count value and the resolution returned from [`Self::get_resolution`], you can
435 /// convert the count value into seconds.
436 ///
437 /// # Errors
438 ///
439 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
440 /// - `ESP_FAIL`: Failed because of other error
441 pub fn get_raw_count(&self) -> Result<u64, EspError> {
442 let mut value: u64 = 0;
443 esp!(unsafe { gptimer_get_raw_count(self.handle, &raw mut value) })?;
444 Ok(value)
445 }
446
447 /// Return the real resolution of the timer.
448 ///
449 /// Usually the timer resolution is same as what you configured in the [`TimerConfig::resolution`],
450 /// but some unstable clock source (e.g. `RC_FAST`) will do a calibration, the real resolution can
451 /// be different from the configured one.
452 ///
453 /// # Errors
454 ///
455 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
456 /// - `ESP_FAIL`: Failed because of other error
457 #[cfg(esp_idf_version_at_least_5_1_0)]
458 #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
459 pub fn get_resolution(&self) -> Result<Hertz, EspError> {
460 let mut value: u32 = 0;
461 esp!(unsafe { gptimer_get_resolution(self.handle, &raw mut value) })?;
462 Ok(value.Hz())
463 }
464
465 /// Get GPTimer captured count value.
466 ///
467 /// Different from [`Self::get_raw_count`], this function won't trigger a software capture event.
468 /// It just returns the last captured count value. It's especially useful when the capture has
469 /// already been triggered by an external event and you want to read the captured value.
470 ///
471 /// # Errors
472 ///
473 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
474 /// - `ESP_FAIL`: Failed because of other error
475 #[cfg(esp_idf_version_at_least_5_1_0)]
476 #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
477 pub fn get_captured_count(&self) -> Result<u64, EspError> {
478 let mut value: u64 = 0;
479 esp!(unsafe { gptimer_get_captured_count(self.handle, &raw mut value) })?;
480 Ok(value)
481 }
482
483 /// Define the ISR handler for when the alarm event occurs.
484 ///
485 /// The callbacks are expected to run in ISR context.
486 /// The first call to this function should happen before the timer is enabled
487 /// through [`Self::enable`].
488 ///
489 /// There is only one callback possible, you can not subscribe multiple callbacks.
490 ///
491 /// # ISR Safety
492 ///
493 /// Care should be taken not to call std, libc or FreeRTOS APIs (except for a few allowed ones)
494 /// in the callback passed to this function, as it is executed in an ISR context.
495 ///
496 /// You are not allowed to block, but you are allowed to call FreeRTOS APIs with the FromISR suffix.
497 ///
498 /// # Errors
499 ///
500 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument
501 /// - `ESP_ERR_INVALID_STATE`: Failed because the timer is not in init state
502 /// - `ESP_FAIL`: Failed because of other error
503 pub fn subscribe(
504 &mut self,
505 on_alarm: impl FnMut(AlarmEventData) + Send + 'static,
506 ) -> Result<(), EspError> {
507 unsafe { self.subscribe_nonstatic(on_alarm) }
508 }
509
510 /// Subscribe a non-'static callback for when a transmission is done.
511 ///
512 /// # Safety
513 ///
514 /// You must not forget the driver (for example through [`core::mem::forget`]), while the callback
515 /// is still subscribed, otherwise this would lead to undefined behavior.
516 ///
517 /// To unsubscribe the callback, call [`Self::unsubscribe`].
518 pub unsafe fn subscribe_nonstatic(
519 &mut self,
520 on_alarm: impl FnMut(AlarmEventData) + Send + 'd,
521 ) -> Result<(), EspError> {
522 let mut user_data = Box::new(AlarmUserData {
523 on_alarm: Box::new(on_alarm),
524 notif: HalIsrNotification::new(),
525 });
526 let cbs = gptimer_event_callbacks_t {
527 on_alarm: Some(Self::handle_isr),
528 };
529
530 esp!(unsafe {
531 gptimer_register_event_callbacks(self.handle, &cbs, (&raw mut *user_data) as *mut _)
532 })?;
533
534 // Store the user data in the struct to prevent it from being freed too early
535 self.on_alarm = Some(user_data);
536
537 Ok(())
538 }
539
540 /// Register the default callback.
541 ///
542 /// This function will overwrite any previously registered callbacks.
543 /// This is useful if you want to asynchronously wait for an alarm event
544 /// through [`Self::wait`] or [`Self::delay`].
545 pub fn subscribe_default(&mut self) -> Result<(), EspError> {
546 self.subscribe(|_| {})
547 }
548
549 /// Unregister the previously registered callback.
550 pub fn unsubscribe(&mut self) -> Result<(), EspError> {
551 esp!(unsafe {
552 gptimer_register_event_callbacks(self.handle, ptr::null(), ptr::null_mut())
553 })?;
554 self.on_alarm = None;
555
556 Ok(())
557 }
558
559 /// Set alarm event actions for GPTimer.
560 ///
561 /// If the config is `None`, the alarm will be disabled.
562 ///
563 /// # Errors
564 ///
565 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
566 /// - `ESP_FAIL`: Failed because of other error
567 pub fn set_alarm_action(&self, config: Option<&AlarmConfig>) -> Result<(), EspError> {
568 let sys_config = config.map(|c| c.into());
569
570 esp!(unsafe {
571 gptimer_set_alarm_action(self.handle, sys_config.as_ref().map_or(ptr::null(), |c| c))
572 })
573 }
574
575 /// Enable the timer.
576 ///
577 /// This function will transition the timer from the "init" state to "enable" state.
578 ///
579 /// # Note
580 ///
581 /// This function will enable the interrupt service, if a callback has been registered
582 /// through [`Self::subscribe`].
583 ///
584 /// It will acquire a power management lock, if a specific source clock (e.g. APB) is selected
585 /// in the timer configuration, while `CONFIG_PM_ENABLE` is set in the project configuration.
586 ///
587 /// To make the timer start counting, call [`Self::start`].
588 ///
589 /// # Errors
590 ///
591 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
592 /// - `ESP_ERR_INVALID_STATE`: Failed because the timer is not in "init" state (e.g. already enabled)
593 /// - `ESP_FAIL`: Failed because of other error
594 pub fn enable(&self) -> Result<(), EspError> {
595 esp!(unsafe { gptimer_enable(self.handle) })
596 }
597
598 /// Disable the timer.
599 ///
600 /// This function will transition the timer from the "enable" state to "init" state.
601 ///
602 /// # Note
603 ///
604 /// This function will disable the interrupt service, if a callback has been registered
605 /// through [`Self::subscribe`].
606 ///
607 /// It will release the power management lock, if it acquired one in [`Self::enable`].
608 ///
609 /// Disabling the timer will not make it stop counting.
610 /// To make the timer stop counting, call [`Self::stop`].
611 ///
612 /// # Errors
613 ///
614 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
615 /// - `ESP_ERR_INVALID_STATE`: Failed because the timer is not in "enable" state (e.g. already disabled)
616 /// - `ESP_FAIL`: Failed because of other error
617 pub fn disable(&self) -> Result<(), EspError> {
618 esp!(unsafe { gptimer_disable(self.handle) })
619 }
620
621 /// Start GPTimer (internal counter starts counting)
622 ///
623 /// This function will transition the timer from the "enable" state to "run" state.
624 ///
625 /// # Errors
626 ///
627 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
628 /// - `ESP_ERR_INVALID_STATE`: Failed because the timer is not enabled or already in running
629 /// - `ESP_FAIL`: Failed because of other error
630 pub fn start(&self) -> Result<(), EspError> {
631 esp!(unsafe { gptimer_start(self.handle) })
632 }
633
634 /// Stop GPTimer (internal counter stops counting)
635 ///
636 /// This function will transition the timer from the "run" state to "enable" state.
637 ///
638 /// # Errors
639 ///
640 /// - `ESP_ERR_INVALID_ARG`: Failed because of invalid argument (should not happen)
641 /// - `ESP_ERR_INVALID_STATE`: Failed because the timer is not in running.
642 /// - `ESP_FAIL`: Failed because of other error
643 pub fn stop(&self) -> Result<(), EspError> {
644 esp!(unsafe { gptimer_stop(self.handle) })
645 }
646
647 unsafe extern "C" fn handle_isr(
648 _handle: gptimer_handle_t,
649 event_data: *const gptimer_alarm_event_data_t,
650 arg: *mut c_void,
651 ) -> bool {
652 let user_data = &mut *(arg as *mut AlarmUserData);
653 let event = AlarmEventData::from(*event_data);
654
655 interrupt::with_isr_yield_signal(|| {
656 (user_data.on_alarm)(event);
657
658 user_data.notif.notify_lsb();
659 })
660 }
661}
662
663// SAFETY: According to the ESP-IDF docs, the driver can be used in a multi-threaded context
664// without extra locking.
665unsafe impl<'d> Send for TimerDriver<'d> {}
666unsafe impl<'d> Sync for TimerDriver<'d> {}
667
668impl<'d> Drop for TimerDriver<'d> {
669 fn drop(&mut self) {
670 // Timer must be from run -> enable state first before it is disabled
671 let _ = self.stop();
672 // Timer must be in "init" state before deletion (= disable state)
673 let _ = self.disable();
674
675 unsafe {
676 gptimer_del_timer(self.handle);
677 }
678 }
679}
680
681impl<'d> fmt::Debug for TimerDriver<'d> {
682 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683 f.debug_struct("TimerDriver")
684 .field("handle", &self.handle)
685 .finish()
686 }
687}