Skip to content

[2/3] Timer abstraction: add OneShotTimer/PeriodicTimer drivers, Timer trait #1570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 23, 2024
7 changes: 3 additions & 4 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*};
219 changes: 219 additions & 0 deletions esp-hal/src/timer/mod.rs
Original file line number Diff line number Diff line change
@@ -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<u64, 1, 1_000_000>;

/// 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<T> {
inner: T,
}

impl<T> OneShotTimer<T>
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<T, UXX> embedded_hal_02::blocking::delay::DelayMs<UXX> for OneShotTimer<T>
where
T: Timer,
UXX: Into<u32>,
{
fn delay_ms(&mut self, ms: UXX) {
self.delay_millis(ms.into());
}
}

#[cfg(feature = "embedded-hal-02")]
impl<T, UXX> embedded_hal_02::blocking::delay::DelayUs<UXX> for OneShotTimer<T>
where
T: Timer,
UXX: Into<u32>,
{
fn delay_us(&mut self, us: UXX) {
self.delay_micros(us.into());
}
}

#[cfg(feature = "embedded-hal")]
impl<T> embedded_hal::delay::DelayNs for OneShotTimer<T>
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<T> {
inner: T,
}

impl<T> PeriodicTimer<T>
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<T> embedded_hal_02::timer::CountDown for PeriodicTimer<T>
where
T: Timer,
{
type Time = MicrosDurationU64;

fn start<Time>(&mut self, timeout: Time)
where
Time: Into<Self::Time>,
{
self.start(timeout.into());
}

fn wait(&mut self) -> nb::Result<(), void::Void> {
self.wait()
}
}

#[cfg(feature = "embedded-hal-02")]
impl<T> embedded_hal_02::timer::Cancel for PeriodicTimer<T>
where
T: Timer,
{
type Error = Error;

fn cancel(&mut self) -> Result<(), Self::Error> {
self.cancel()
}
}

#[cfg(feature = "embedded-hal-02")]
impl<T> embedded_hal_02::timer::Periodic for PeriodicTimer<T> where T: Timer {}
Loading
Loading