Skip to content
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

Type-erase timers by default #2136

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Changed the parameters of `Spi::with_pins` to no longer be optional (#2133)
- Renamed `DummyPin` to `NoPin` and removed all internal logic from it. (#2133)
- The `NO_PIN` constant has been removed. (#2133)
- `PeriodicTimer` and `OneShotTimer` are now type-erased by default. Use the new `new_typed` constructor to keep using the ZST pin types. (#?)

### Fixed

Expand Down
12 changes: 12 additions & 0 deletions esp-hal/MIGRATING-0.20.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ have been removed. Use `new` or `new_async` instead.

The `ErasedTimer` has been renamed to `AnyTimer`.

### Type-erased timers

`PeriodicTimer` and `OneShotTimer` are now type-erased by default. This unfortunately requires the
timer to implement `Into<AnyTimer>`. In case you want to use a timer that does not (for example
because you've used the async timer APIs of the same instance before), you can use the `new_typed`
constructor.

```rust
let timer = PeriodicTimer::new(timg0.timer0); // timer will have the type `PeriodicTimer<'some>` (or `PeriodicTimer<'some, AnyTimer>` if you want to be explicit about it)
let timer = PeriodicTimer::new_typed(timg0.timer0); // timer will have the type `PeriodicTimer<'some, Timer<Timer0<TIMG0>, Blocking>>`
```

### `esp_hal::time::current_time` rename

To avoid confusion with the `Rtc::current_time` wall clock time APIs, we've renamed `esp_hal::time::current_time` to `esp_hal::time::now()`.
Expand Down
182 changes: 92 additions & 90 deletions esp-hal/src/timer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,26 @@ pub trait Timer: crate::private::Sealed {
}

/// A one-shot timer.
pub struct OneShotTimer<'d, T> {
pub struct OneShotTimer<'d, T = AnyTimer> {
inner: PeripheralRef<'d, T>,
}

impl<'d> OneShotTimer<'d, AnyTimer> {
/// Construct a new instance of [`OneShotTimer`] with the timer
/// type-erased.
pub fn new<T: Timer + Into<AnyTimer>>(mut inner: impl Peripheral<P = T> + 'd) -> Self {
let inner = unsafe { inner.clone_unchecked() }.into();

Self::new_typed(inner)
}
}

impl<'d, T> OneShotTimer<'d, T>
where
T: Timer,
{
/// Construct a new instance of [`OneShotTimer`].
pub fn new(inner: impl Peripheral<P = T> + 'd) -> Self {
pub fn new_typed(inner: impl Peripheral<P = T> + 'd) -> Self {
crate::into_ref!(inner);

Self { inner }
Expand Down Expand Up @@ -242,16 +252,26 @@ where
}

/// A periodic timer.
pub struct PeriodicTimer<'d, T> {
pub struct PeriodicTimer<'d, T = AnyTimer> {
inner: PeripheralRef<'d, T>,
}

impl<'d> PeriodicTimer<'d, AnyTimer> {
/// Construct a new instance of [`PeriodicTimer`] with the timer
/// type-erased.
pub fn new<T: Timer + Into<AnyTimer>>(mut inner: impl Peripheral<P = T> + 'd) -> Self {
let inner = unsafe { inner.clone_unchecked() }.into();

Self::new_typed(inner)
}
}

impl<'d, T> PeriodicTimer<'d, T>
where
T: Timer,
{
/// Construct a new instance of [`PeriodicTimer`].
pub fn new(inner: impl Peripheral<P = T> + 'd) -> Self {
pub fn new_typed(inner: impl Peripheral<P = T> + 'd) -> Self {
crate::into_ref!(inner);

Self { inner }
Expand Down Expand Up @@ -357,25 +377,79 @@ where

impl<'d, T> embedded_hal_02::timer::Periodic for PeriodicTimer<'d, T> where T: Timer {}

/// An enum of all timer types
enum AnyTimerInner {
/// Timer 0 of the TIMG0 peripheral in blocking mode.
Timg0Timer0(timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>),
/// Timer 1 of the TIMG0 peripheral in blocking mode.
macro_rules! any_timer {
($ty:path => $var:ident) => {
impl $ty {
/// Convert this timer into an [`AnyTimer`].
pub fn degrade(self) -> AnyTimer {
AnyTimer(AnyTimerInner::$var(self))
}
}

impl From<$ty> for AnyTimer {
fn from(value: $ty) -> Self {
value.degrade()
}
}
};

(
$(
$(#[$cfg:meta])?
$ty:path => $var:ident,
)*
) => {
/// An enum of all timer types
enum AnyTimerInner {
$(
$(#[$cfg])?
$var($ty),
)*
}

$(
$(#[$cfg])?
any_timer!($ty => $var);
)*

impl Timer for AnyTimer {
delegate::delegate! {
to match &self.0 {
$(
$(#[$cfg])?
AnyTimerInner::$var(inner) => inner,
)*
} {
fn start(&self);
fn stop(&self);
fn reset(&self);
fn is_running(&self) -> bool;
fn now(&self) -> Instant<u64, 1, 1_000_000>;
fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error>;
fn enable_auto_reload(&self, auto_reload: bool);
fn enable_interrupt(&self, state: bool);
fn clear_interrupt(&self);
fn set_interrupt_handler(&self, handler: InterruptHandler);
fn is_interrupt_set(&self) -> bool;
fn set_alarm_active(&self, state: bool);
}
}
}
};
}

any_timer! {
timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking> => Timg0Timer0,
#[cfg(timg_timer1)]
Timg0Timer1(timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking>),
/// Timer 0 of the TIMG1 peripheral in blocking mode.
timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking> => Timg0Timer1,
#[cfg(timg1)]
Timg1Timer0(timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking>),
/// Timer 1 of the TIMG1 peripheral in blocking mode.
timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking> => Timg1Timer0,
#[cfg(all(timg1, timg_timer1))]
Timg1Timer1(timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking>),
/// Systimer Alarm in periodic mode with blocking behavior.
timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking> => Timg1Timer1,
#[cfg(systimer)]
SystimerAlarmPeriodic(systimer::Alarm<'static, systimer::Periodic, Blocking>),
/// Systimer Target in periodic mode with blocking behavior.
systimer::Alarm<'static, systimer::Periodic, Blocking> => SystimerAlarmPeriodic,
#[cfg(systimer)]
SystimerAlarmTarget(systimer::Alarm<'static, systimer::Target, Blocking>),
systimer::Alarm<'static, systimer::Target, Blocking> => SystimerAlarmTarget,
}

/// A type-erased timer
Expand All @@ -385,78 +459,6 @@ pub struct AnyTimer(AnyTimerInner);

impl crate::private::Sealed for AnyTimer {}

impl From<timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>> for AnyTimer {
fn from(value: timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>) -> Self {
Self(AnyTimerInner::Timg0Timer0(value))
}
}

#[cfg(timg_timer1)]
impl From<timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking>> for AnyTimer {
fn from(value: timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking>) -> Self {
Self(AnyTimerInner::Timg0Timer1(value))
}
}

#[cfg(timg1)]
impl From<timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking>> for AnyTimer {
fn from(value: timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking>) -> Self {
Self(AnyTimerInner::Timg1Timer0(value))
}
}

#[cfg(all(timg1, timg_timer1))]
impl From<timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking>> for AnyTimer {
fn from(value: timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking>) -> Self {
Self(AnyTimerInner::Timg1Timer1(value))
}
}

#[cfg(systimer)]
impl From<systimer::Alarm<'static, systimer::Periodic, Blocking>> for AnyTimer {
fn from(value: systimer::Alarm<'static, systimer::Periodic, Blocking>) -> Self {
Self(AnyTimerInner::SystimerAlarmPeriodic(value))
}
}

#[cfg(systimer)]
impl From<systimer::Alarm<'static, systimer::Target, Blocking>> for AnyTimer {
fn from(value: systimer::Alarm<'static, systimer::Target, Blocking>) -> Self {
Self(AnyTimerInner::SystimerAlarmTarget(value))
}
}

impl Timer for AnyTimer {
delegate::delegate! {
to match &self.0 {
AnyTimerInner::Timg0Timer0(inner) => inner,
#[cfg(timg_timer1)]
AnyTimerInner::Timg0Timer1(inner) => inner,
#[cfg(timg1)]
AnyTimerInner::Timg1Timer0(inner) => inner,
#[cfg(all(timg1,timg_timer1))]
AnyTimerInner::Timg1Timer1(inner) => inner,
#[cfg(systimer)]
AnyTimerInner::SystimerAlarmPeriodic(inner) => inner,
#[cfg(systimer)]
AnyTimerInner::SystimerAlarmTarget(inner) => inner,
} {
fn start(&self);
fn stop(&self);
fn reset(&self);
fn is_running(&self) -> bool;
fn now(&self) -> Instant<u64, 1, 1_000_000>;
fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error>;
fn enable_auto_reload(&self, auto_reload: bool);
fn enable_interrupt(&self, state: bool);
fn clear_interrupt(&self);
fn set_interrupt_handler(&self, handler: InterruptHandler);
fn is_interrupt_set(&self) -> bool;
fn set_alarm_active(&self, state: bool);
}
}
}

impl Peripheral for AnyTimer {
type P = Self;

Expand Down
6 changes: 2 additions & 4 deletions examples/src/bin/embassy_multicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use esp_hal::{
cpu_control::{CpuControl, Stack},
get_core,
gpio::{Io, Level, Output, Pin},
timer::{timg::TimerGroup, AnyTimer},
timer::timg::TimerGroup,
};
use esp_hal_embassy::Executor;
use esp_println::println;
Expand Down Expand Up @@ -56,9 +56,7 @@ async fn main(_spawner: Spawner) {
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);

let timg0 = TimerGroup::new(peripherals.TIMG0);
let timer0: AnyTimer = timg0.timer0.into();
let timer1: AnyTimer = timg0.timer1.into();
esp_hal_embassy::init([timer0, timer1]);
esp_hal_embassy::init([timg0.timer0.degrade(), timg0.timer1.degrade()]);

let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL);

Expand Down
6 changes: 2 additions & 4 deletions examples/src/bin/embassy_multicore_interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use esp_hal::{
gpio::{Io, Level, Output, Pin},
interrupt::{software::SoftwareInterruptControl, Priority},
prelude::*,
timer::{timg::TimerGroup, AnyTimer},
timer::timg::TimerGroup,
};
use esp_hal_embassy::InterruptExecutor;
use esp_println::println;
Expand Down Expand Up @@ -78,9 +78,7 @@ fn main() -> ! {
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);

let timg0 = TimerGroup::new(peripherals.TIMG0);
let timer0: AnyTimer = timg0.timer0.into();
let timer1: AnyTimer = timg0.timer1.into();
esp_hal_embassy::init([timer0, timer1]);
esp_hal_embassy::init([timg0.timer0.degrade(), timg0.timer1.degrade()]);

let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL);

Expand Down
9 changes: 4 additions & 5 deletions examples/src/bin/embassy_multiprio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use embassy_time::{Duration, Instant, Ticker, Timer};
use esp_backtrace as _;
use esp_hal::{
interrupt::{software::SoftwareInterruptControl, Priority},
timer::{timg::TimerGroup, AnyTimer},
timer::timg::TimerGroup,
};
use esp_hal_embassy::InterruptExecutor;
use esp_println::println;
Expand Down Expand Up @@ -76,20 +76,19 @@ async fn main(low_prio_spawner: Spawner) {
let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);

let timg0 = TimerGroup::new(peripherals.TIMG0);
let timer0: AnyTimer = timg0.timer0.into();

cfg_if::cfg_if! {
if #[cfg(feature = "esp32c2")] {
use esp_hal::timer::systimer::{SystemTimer, Target};
let systimer = SystemTimer::new(peripherals.SYSTIMER).split::<Target>();
let timer1: AnyTimer = systimer.alarm0.into();
let timer1 = systimer.alarm0;
} else {
let timg1 = TimerGroup::new(peripherals.TIMG1);
let timer1: AnyTimer = timg1.timer0.into();
let timer1 = timg1.timer0;
}
}

esp_hal_embassy::init([timer0, timer1]);
esp_hal_embassy::init([timg0.timer0.degrade(), timer1.degrade()]);

static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
let executor = InterruptExecutor::new(sw_ints.software_interrupt2);
Expand Down
12 changes: 2 additions & 10 deletions examples/src/bin/wifi_80211_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ use core::marker::PhantomData;

use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{
delay::Delay,
prelude::*,
rng::Rng,
timer::{timg::TimerGroup, AnyTimer, PeriodicTimer},
};
use esp_hal::{delay::Delay, prelude::*, rng::Rng, timer::timg::TimerGroup};
use esp_wifi::{initialize, wifi, EspWifiInitFor};
use ieee80211::{
common::{CapabilitiesInformation, FCFFlags},
Expand Down Expand Up @@ -46,12 +41,9 @@ fn main() -> ! {
let delay = Delay::new();

let timg0 = TimerGroup::new(peripherals.TIMG0);
let timer0: AnyTimer = timg0.timer0.into();
let timer = PeriodicTimer::new(timer0);

let init = initialize(
EspWifiInitFor::Wifi,
timer,
timg0.timer0,
Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
)
Expand Down
11 changes: 2 additions & 9 deletions examples/src/bin/wifi_sniffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ use core::cell::RefCell;

use critical_section::Mutex;
use esp_backtrace as _;
use esp_hal::{
prelude::*,
rng::Rng,
timer::{timg::TimerGroup, AnyTimer, PeriodicTimer},
};
use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup};
use esp_println::println;
use esp_wifi::{initialize, wifi, EspWifiInitFor};
use ieee80211::{match_frames, mgmt_frame::BeaconFrame};
Expand All @@ -41,12 +37,9 @@ fn main() -> ! {
esp_alloc::heap_allocator!(72 * 1024);

let timg0 = TimerGroup::new(peripherals.TIMG0);
let timer0: AnyTimer = timg0.timer0.into();
let timer = PeriodicTimer::new(timer0);

let init = initialize(
EspWifiInitFor::Wifi,
timer,
timg0.timer0,
Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
)
Expand Down
Loading