diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index bc4ff2dc6..1e3c342a2 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -7,17 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Updated example on i2c to use the new `interrupt_handler` parameter (#1376) - ### Added -- i2c: implement `I2C:transaction` for `embedded-hal` and `embedded-hal-async` -- spi: implement `with_bit_order` - #1537 +- i2c: implement `I2C:transaction` for `embedded-hal` and `embedded-hal-async` (#1505) +- spi: implement `with_bit_order` (#1537) - ESP32-PICO-V3-02: Initial support (#1155) - `time::current_time` API (#1503) - ESP32-S3: Add LCD_CAM Camera driver (#1483) - `embassy-usb` support (#1517) - SPI Slave support for ESP32-S2 (#1562) +- Add new generic `OneShotTimer` and `PeriodicTimer` drivers, plus new `Timer` trait which is implemented for `TIMGx` and `SYSTIMER` (#1570) ### Fixed diff --git a/esp-hal/src/prelude.rs b/esp-hal/src/prelude.rs index 83d5ba67d..2f3c4df69 100644 --- a/esp-hal/src/prelude.rs +++ b/esp-hal/src/prelude.rs @@ -36,6 +36,8 @@ pub use crate::timer::timg::{ Instance as _esp_hal_timer_timg_Instance, TimerGroupInstance as _esp_hal_timer_timg_TimerGroupInstance, }; +#[cfg(any(systimer, timg0, timg1))] +pub use crate::timer::Timer as _esp_hal_timer_Timer; #[cfg(any(uart0, uart1, uart2))] pub use crate::uart::{Instance as _esp_hal_uart_Instance, UartPins as _esp_hal_uart_UartPins}; pub use crate::{entry, macros::*}; diff --git a/esp-hal/src/timer/mod.rs b/esp-hal/src/timer/mod.rs index 171591875..1064150a2 100644 --- a/esp-hal/src/timer/mod.rs +++ b/esp-hal/src/timer/mod.rs @@ -1,6 +1,225 @@ //! General-purpose timers. +use fugit::{ExtU64, Instant, MicrosDurationU64}; + #[cfg(systimer)] pub mod systimer; #[cfg(any(timg0, timg1))] pub mod timg; + +/// Timer errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The timer is already active. + TimerActive, + /// The timer is not currently active. + TimerInactive, + /// The alarm is not currently active. + AlarmInactive, +} + +/// Functionality provided by any timer peripheral. +pub trait Timer: crate::private::Sealed { + /// Start the timer. + fn start(&self); + + /// Stop the timer. + fn stop(&self); + + /// Reset the timer value to 0. + fn reset(&self); + + /// Is the timer running? + fn is_running(&self) -> bool; + + /// The current timer value. + fn now(&self) -> Instant; + + /// Load a target value into the timer. + fn load_value(&self, value: MicrosDurationU64); + + /// Enable auto reload of the loaded value. + fn enable_auto_reload(&self, auto_reload: bool); + + /// Enable or disable the timer's interrupt. + fn enable_interrupt(&self, state: bool); + + /// Clear the timer's interrupt. + fn clear_interrupt(&self); + + /// Has the timer triggered? + fn is_interrupt_set(&self) -> bool; + + /// FIXME: This is (hopefully?) temporary... + fn set_alarm_active(&self, state: bool); +} + +/// A one-shot timer. +pub struct OneShotTimer { + inner: T, +} + +impl OneShotTimer +where + T: Timer, +{ + /// Construct a new instance of [`OneShotTimer`]. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Pauses execution for *at least* `ms` milliseconds. + pub fn delay_millis(&self, ms: u32) { + self.delay((ms as u64).millis()); + } + + /// Pauses execution for *at least* `us` microseconds. + pub fn delay_micros(&self, us: u32) { + self.delay((us as u64).micros()); + } + + /// Pauses execution for *at least* `ns` nanoseconds. + pub fn delay_nanos(&self, ns: u32) { + self.delay((ns as u64 / 1000).micros()) + } + + fn delay(&self, us: MicrosDurationU64) { + if self.inner.is_running() { + self.inner.stop(); + } + + self.inner.clear_interrupt(); + self.inner.reset(); + + self.inner.enable_auto_reload(false); + self.inner.load_value(us); + self.inner.start(); + + while !self.inner.is_interrupt_set() { + // Wait + } + + self.inner.stop(); + self.inner.clear_interrupt(); + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::blocking::delay::DelayMs for OneShotTimer +where + T: Timer, + UXX: Into, +{ + fn delay_ms(&mut self, ms: UXX) { + self.delay_millis(ms.into()); + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::blocking::delay::DelayUs for OneShotTimer +where + T: Timer, + UXX: Into, +{ + fn delay_us(&mut self, us: UXX) { + self.delay_micros(us.into()); + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal::delay::DelayNs for OneShotTimer +where + T: Timer, +{ + #[allow(clippy::useless_conversion)] + fn delay_ns(&mut self, ns: u32) { + self.delay_nanos(ns.into()); + } +} + +/// A periodic timer. +pub struct PeriodicTimer { + inner: T, +} + +impl PeriodicTimer +where + T: Timer, +{ + /// Construct a new instance of [`PeriodicTimer`]. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Start a new count down. + pub fn start(&mut self, timeout: MicrosDurationU64) { + if self.inner.is_running() { + self.inner.stop(); + } + + self.inner.clear_interrupt(); + self.inner.reset(); + + self.inner.enable_auto_reload(true); + self.inner.load_value(timeout); + self.inner.start(); + } + + /// "Wait" until the count down finishes without blocking. + pub fn wait(&mut self) -> nb::Result<(), void::Void> { + if self.inner.is_interrupt_set() { + self.inner.clear_interrupt(); + self.inner.set_alarm_active(true); // FIXME: Remove if/when able + + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + /// Tries to cancel the active count down. + pub fn cancel(&mut self) -> Result<(), Error> { + if !self.inner.is_running() { + return Err(Error::TimerInactive); + } + + self.inner.stop(); + + Ok(()) + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::timer::CountDown for PeriodicTimer +where + T: Timer, +{ + type Time = MicrosDurationU64; + + fn start