diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 048c36d39..90ce49582 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,9 +15,8 @@ use crate::gpio::sealed::Pin as _; use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; use crate::pac; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; -use crate::timer::Frequency; use crate::timer::Instance as TimerInstance; -use crate::timer::Timer; +use crate::timer::{Frequency, Timer}; use crate::uarte::{Config, Instance as UarteInstance}; // Re-export SVD variants to allow user to directly set values @@ -85,7 +84,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { let r = U::regs(); - let mut timer = Timer::new_irqless(timer); + let mut timer = Timer::new(timer); rxd.conf().write(|w| w.input().connect().drive().h0h1()); r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index eab9a1416..638fd8229 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -27,6 +27,8 @@ pub(crate) mod sealed { fn waker(n: usize) -> &'static AtomicWaker; } pub trait ExtendedInstance {} + + pub trait TimerType {} } pub trait Instance: Unborrow + sealed::Instance + 'static + Send { @@ -84,12 +86,23 @@ pub enum Frequency { /// /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter /// or trigger an event when the counter reaches a certain value. -pub struct Timer<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, + +pub trait TimerType: sealed::TimerType {} + +pub enum Awaitable {} +pub enum NotAwaitable {} + +impl sealed::TimerType for Awaitable {} +impl sealed::TimerType for NotAwaitable {} +impl TimerType for Awaitable {} +impl TimerType for NotAwaitable {} + +pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { + phantom: PhantomData<(&'d mut T, I)>, } -impl<'d, T: Instance> Timer<'d, T> { - pub fn new( +impl<'d, T: Instance> Timer<'d, T, Awaitable> { + pub fn new_awaitable( timer: impl Unborrow + 'd, irq: impl Unborrow + 'd, ) -> Self { @@ -101,11 +114,22 @@ impl<'d, T: Instance> Timer<'d, T> { Self::new_irqless(timer) } - +} +impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. /// - /// This is used by `Uarte` internally. - pub(crate) fn new_irqless(_timer: impl Unborrow + 'd) -> Self { + /// This can be useful for triggering tasks via PPI + /// `Uarte` uses this internally. + pub fn new(timer: impl Unborrow + 'd) -> Self { + Self::new_irqless(timer) + } +} + +impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { + /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. + /// + /// This is used by the public constructors. + fn new_irqless(_timer: impl Unborrow + 'd) -> Self { let regs = T::regs(); let mut this = Self { @@ -208,7 +232,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// # Panics /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). - pub fn cc(&mut self, n: usize) -> Cc { + pub fn cc(&mut self, n: usize) -> Cc { if n >= T::CCS { panic!( "Cannot get CC register {} of timer with {} CC registers.", @@ -230,12 +254,48 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register -pub struct Cc<'a, T: Instance> { +pub struct Cc<'a, T: Instance, I: TimerType = NotAwaitable> { n: usize, - phantom: PhantomData<&'a mut T>, + phantom: PhantomData<(&'a mut T, I)>, } -impl<'a, T: Instance> Cc<'a, T> { +impl<'a, T: Instance> Cc<'a, T, Awaitable> { + /// Wait until the timer's counter reaches the value stored in this register. + /// + /// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`. + pub async fn wait(&mut self) { + let regs = T::regs(); + + // Enable the interrupt for this CC's COMPARE event. + regs.intenset + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); + + // Disable the interrupt if the future is dropped. + let on_drop = OnDrop::new(|| { + regs.intenclr + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); + }); + + poll_fn(|cx| { + T::waker(self.n).register(cx.waker()); + + if regs.events_compare[self.n].read().bits() != 0 { + // Reset the register for next time + regs.events_compare[self.n].reset(); + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + // The interrupt was already disabled in the interrupt handler, so there's no need to disable it again. + on_drop.defuse(); + } +} +impl<'a, T: Instance> Cc<'a, T, NotAwaitable> {} + +impl<'a, T: Instance, I: TimerType> Cc<'a, T, I> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { T::regs().cc[self.n].read().cc().bits() @@ -304,37 +364,4 @@ impl<'a, T: Instance> Cc<'a, T> { .shorts .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (8 + self.n))) }) } - - /// Wait until the timer's counter reaches the value stored in this register. - /// - /// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`. - pub async fn wait(&mut self) { - let regs = T::regs(); - - // Enable the interrupt for this CC's COMPARE event. - regs.intenset - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); - - // Disable the interrupt if the future is dropped. - let on_drop = OnDrop::new(|| { - regs.intenclr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); - }); - - poll_fn(|cx| { - T::waker(self.n).register(cx.waker()); - - if regs.events_compare[self.n].read().bits() != 0 { - // Reset the register for next time - regs.events_compare[self.n].reset(); - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - // The interrupt was already disabled in the interrupt handler, so there's no need to disable it again. - on_drop.defuse(); - } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index b2b298661..a6909be68 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -18,9 +18,8 @@ use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin}; use crate::interrupt::Interrupt; use crate::pac; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; -use crate::timer::Frequency; use crate::timer::Instance as TimerInstance; -use crate::timer::Timer; +use crate::timer::{Frequency, Timer}; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; @@ -318,7 +317,7 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { ) -> Self { let baudrate = config.baudrate; let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config); - let mut timer = Timer::new_irqless(timer); + let mut timer = Timer::new(timer); unborrow!(ppi_ch1, ppi_ch2); diff --git a/examples/nrf/src/bin/awaitable_timer.rs b/examples/nrf/src/bin/awaitable_timer.rs new file mode 100644 index 000000000..289a33c71 --- /dev/null +++ b/examples/nrf/src/bin/awaitable_timer.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; +use embassy_nrf::interrupt; +use embassy_nrf::timer::Timer; +use embassy_nrf::Peripherals; +use example_common::info; + +use embassy::executor::Spawner; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); + // default frequency is 1MHz, so this triggers every second + t.cc(0).write(1_000_000); + // clear the timer value on cc[0] compare match + t.cc(0).short_compare_clear(); + t.start(); + + loop { + // wait for compare match + t.cc(0).wait().await; + info!("hardware timer tick"); + } +}