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

Implement embedded_hal_async::delay::DelayNs for TIMGx timers #2084

Merged
merged 18 commits into from
Sep 24, 2024
Merged
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 @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Expose `RtcClock::get_xtal_freq` and `RtcClock::get_slow_freq` publically for all chips (#2183)
- TWAI support for ESP32-H2 (#2199)
- Added a way to configure watchdogs in `esp_hal::init` (#2180)
- Implement `embedded_hal_async::delay::DelayNs` for `TIMGx` timers (#2084)

### Changed

Expand Down
16 changes: 10 additions & 6 deletions esp-hal/src/timer/systimer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,11 +1029,11 @@ mod asynch {

#[must_use = "futures do nothing unless you `.await` or poll them"]
pub(crate) struct AlarmFuture<'a, COMP: Comparator, UNIT: Unit> {
alarm: &'a Alarm<'a, Periodic, crate::Async, COMP, UNIT>,
alarm: &'a Alarm<'a, Target, crate::Async, COMP, UNIT>,
}

impl<'a, COMP: Comparator, UNIT: Unit> AlarmFuture<'a, COMP, UNIT> {
pub(crate) fn new(alarm: &'a Alarm<'a, Periodic, crate::Async, COMP, UNIT>) -> Self {
pub(crate) fn new(alarm: &'a Alarm<'a, Target, crate::Async, COMP, UNIT>) -> Self {
alarm.clear_interrupt();

let (interrupt, handler) = match alarm.comparator.channel() {
Expand All @@ -1047,6 +1047,8 @@ mod asynch {
interrupt::enable(interrupt, handler.priority()).unwrap();
}

alarm.set_interrupt_handler(handler);

alarm.enable_interrupt(true);

Self { alarm }
Expand Down Expand Up @@ -1076,11 +1078,13 @@ mod asynch {
}

impl<'d, COMP: Comparator, UNIT: Unit> embedded_hal_async::delay::DelayNs
for Alarm<'d, Periodic, crate::Async, COMP, UNIT>
for Alarm<'d, Target, crate::Async, COMP, UNIT>
{
async fn delay_ns(&mut self, ns: u32) {
let period = MicrosDurationU32::from_ticks(ns / 1000);
self.set_period(period);
async fn delay_ns(&mut self, nanos: u32) {
self.set_target(
self.unit.read_count()
+ (nanos as u64 * SystemTimer::ticks_per_second()).div_ceil(1_000_000_000),
);

AlarmFuture::new(self).await;
}
Expand Down
213 changes: 213 additions & 0 deletions esp-hal/src/timer/timg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,57 @@ where
{
/// Construct a new instance of [`TimerGroup`] in asynchronous mode
pub fn new_async(_timer_group: impl Peripheral<P = T> + 'd) -> Self {
match T::id() {
0 => {
use crate::timer::timg::asynch::timg0_timer0_handler;
unsafe {
interrupt::bind_interrupt(
Interrupt::TG0_T0_LEVEL,
timg0_timer0_handler.handler(),
);
interrupt::enable(Interrupt::TG0_T0_LEVEL, timg0_timer0_handler.priority())
.unwrap();

#[cfg(timg_timer1)]
{
use crate::timer::timg::asynch::timg0_timer1_handler;

interrupt::bind_interrupt(
Interrupt::TG0_T1_LEVEL,
timg0_timer1_handler.handler(),
);
interrupt::enable(Interrupt::TG0_T1_LEVEL, timg0_timer1_handler.priority())
.unwrap();
}
}
}
#[cfg(timg1)]
1 => {
use crate::timer::timg::asynch::timg1_timer0_handler;
unsafe {
{
interrupt::bind_interrupt(
Interrupt::TG1_T0_LEVEL,
timg1_timer0_handler.handler(),
);
interrupt::enable(Interrupt::TG1_T0_LEVEL, timg1_timer0_handler.priority())
.unwrap();
}
#[cfg(timg_timer1)]
{
use crate::timer::timg::asynch::timg1_timer1_handler;
interrupt::bind_interrupt(
Interrupt::TG1_T1_LEVEL,
timg1_timer1_handler.handler(),
);
interrupt::enable(Interrupt::TG1_T1_LEVEL, timg1_timer1_handler.priority())
.unwrap();
}
}
}
_ => unreachable!(),
}

Self::new_inner(_timer_group)
}
}
Expand Down Expand Up @@ -1040,6 +1091,168 @@ where
}
}

// Async functionality of the timer groups.
mod asynch {
use core::{
pin::Pin,
task::{Context, Poll},
};

use embassy_sync::waitqueue::AtomicWaker;
use procmacros::handler;

use super::*;

cfg_if::cfg_if! {
if #[cfg(all(timg1, timg_timer1))] {
const NUM_WAKERS: usize = 4;
} else if #[cfg(timg1)] {
const NUM_WAKERS: usize = 2;
} else {
const NUM_WAKERS: usize = 1;
}
}

static WAKERS: [AtomicWaker; NUM_WAKERS] = [const { AtomicWaker::new() }; NUM_WAKERS];

pub(crate) struct TimerFuture<'a, T>
where
T: Instance,
{
timer: &'a Timer<T, crate::Async>,
}

impl<'a, T> TimerFuture<'a, T>
where
T: Instance,
{
pub(crate) fn new(timer: &'a Timer<T, crate::Async>) -> Self {
use crate::timer::Timer;

timer.enable_interrupt(true);

Self { timer }
}

fn event_bit_is_clear(&self) -> bool {
self.timer
.register_block()
.int_ena()
.read()
.t(self.timer.timer_number())
.bit_is_clear()
}
}

impl<'a, T> core::future::Future for TimerFuture<'a, T>
JurajSadel marked this conversation as resolved.
Show resolved Hide resolved
where
T: Instance,
{
type Output = ();

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let index = (self.timer.timer_number() << 1) | self.timer.timer_group();
JurajSadel marked this conversation as resolved.
Show resolved Hide resolved
WAKERS[index as usize].register(ctx.waker());

if self.event_bit_is_clear() {
Poll::Ready(())
} else {
Poll::Pending
}
}
}

impl<'a, T> Drop for TimerFuture<'a, T>
where
T: Instance,
{
fn drop(&mut self) {
self.timer.clear_interrupt();
}
}

impl<T> embedded_hal_async::delay::DelayNs for Timer<T, crate::Async>
where
T: Instance,
{
async fn delay_ns(&mut self, ns: u32) {
use crate::timer::Timer as _;

let period = MicrosDurationU64::from_ticks(ns.div_ceil(1000) as u64);
self.load_value(period).unwrap();
self.start();
self.listen();
JurajSadel marked this conversation as resolved.
Show resolved Hide resolved

TimerFuture::new(self).await;
}
}

// INT_ENA means that when the interrupt occurs, it will show up in the INT_ST.
// Clearing INT_ENA that it won't show up on INT_ST but if interrupt is
// already there, it won't clear it - that's why we need to clear the INT_CLR as
// well.
#[handler]
pub(crate) fn timg0_timer0_handler() {
lock(&INT_ENA_LOCK[0], || {
unsafe { &*crate::peripherals::TIMG0::PTR }
.int_ena()
.modify(|_, w| w.t(0).clear_bit())
});

unsafe { &*crate::peripherals::TIMG0::PTR }
JurajSadel marked this conversation as resolved.
Show resolved Hide resolved
.int_clr()
.write(|w| w.t(0).clear_bit_by_one());

WAKERS[0].wake();
}

#[cfg(timg1)]
#[handler]
pub(crate) fn timg1_timer0_handler() {
lock(&INT_ENA_LOCK[1], || {
unsafe { &*crate::peripherals::TIMG1::PTR }
.int_ena()
.modify(|_, w| w.t(0).clear_bit())
});
unsafe { &*crate::peripherals::TIMG1::PTR }
.int_clr()
.write(|w| w.t(0).clear_bit_by_one());

WAKERS[1].wake();
}

#[cfg(timg_timer1)]
#[handler]
pub(crate) fn timg0_timer1_handler() {
lock(&INT_ENA_LOCK[0], || {
unsafe { &*crate::peripherals::TIMG0::PTR }
.int_ena()
.modify(|_, w| w.t(1).clear_bit())
});
unsafe { &*crate::peripherals::TIMG0::PTR }
.int_clr()
.write(|w| w.t(1).clear_bit_by_one());

WAKERS[2].wake();
}

#[cfg(all(timg1, timg_timer1))]
#[handler]
pub(crate) fn timg1_timer1_handler() {
lock(&INT_ENA_LOCK[1], || {
unsafe { &*crate::peripherals::TIMG1::PTR }
.int_ena()
.modify(|_, w| w.t(1).clear_bit())
});

unsafe { &*crate::peripherals::TIMG1::PTR }
.int_clr()
.write(|w| w.t(1).clear_bit_by_one());

WAKERS[3].wake();
}
}

/// Event Task Matrix
#[cfg(soc_etm)]
pub mod etm {
Expand Down
14 changes: 9 additions & 5 deletions hil-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ harness = false
name = "delay"
harness = false

[[test]]
name = "delay_async"
harness = false

[[test]]
name = "dma_macros"
harness = false
Expand Down Expand Up @@ -161,20 +165,20 @@ harness = false

[dependencies]
cfg-if = "1.0.0"
critical-section = "1.1.2"
critical-section = "1.1.3"
defmt = "0.3.8"
defmt-rtt = { version = "0.4.1", optional = true }
embassy-futures = "0.1.1"
embassy-sync = "0.6.0"
embassy-time = { version = "0.3.1" }
embassy-time = "0.3.2"
embedded-hal = "1.0.0"
embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = ["unproven"] }
embedded-hal-async = "1.0.0"
embedded-hal-nb = { version = "1.0.0", optional = true }
esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "panic-handler", "defmt", "semihosting"] }
esp-hal = { path = "../esp-hal", features = ["defmt", "digest"], optional = true }
esp-hal-embassy = { path = "../esp-hal-embassy", optional = true }
portable-atomic = "1.6.0"
portable-atomic = "1.7.0"
static_cell = { version = "2.1.0", features = ["nightly"] }

[dev-dependencies]
Expand All @@ -193,8 +197,8 @@ sha1 = { version = "0.10.6", default-features = false }
sha2 = { version = "0.10.8", default-features = false }

[build-dependencies]
esp-build = { version = "0.1.0", path = "../esp-build" }
esp-metadata = { version = "0.3.0", path = "../esp-metadata" }
esp-build = { path = "../esp-build" }
esp-metadata = { path = "../esp-metadata" }

[features]
default = ["embassy"]
Expand Down
Loading