From a0c40562eab224d273a972fdcf5029c585974bc7 Mon Sep 17 00:00:00 2001 From: f_punk Date: Wed, 1 Sep 2021 16:16:56 +0200 Subject: [PATCH 1/3] added typestate to nrf-Timer useful for hooking up the PPI to an Event without needing interrupt tested with buffered_uart example on nRF52840-DK --- embassy-nrf/src/buffered_uarte.rs | 7 +++--- embassy-nrf/src/timer.rs | 42 +++++++++++++++++++++++++------ embassy-nrf/src/uarte.rs | 7 +++--- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 048c36d39..5c9f4270b 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, NotAwaitableTimer}; use crate::uarte::{Config, Instance as UarteInstance}; // Re-export SVD variants to allow user to directly set values @@ -44,7 +43,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> { struct StateInner<'d, U: UarteInstance, T: TimerInstance> { phantom: PhantomData<&'d mut U>, - timer: Timer<'d, T>, + timer: NotAwaitableTimer<'d, T>, _ppi_ch1: Ppi<'d, AnyConfigurableChannel>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, @@ -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 = NotAwaitableTimer::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..939e99ed5 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,11 +86,25 @@ 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 type AwaitableTimer<'d, T> = Timer<'d, T, Awaitable>; +pub type NotAwaitableTimer<'d, T> = Timer<'d, T, NotAwaitable>; + +pub struct Timer<'d, T: Instance, I: TimerType> { + phantom: PhantomData<(&'d mut T, I)>, } -impl<'d, T: Instance> Timer<'d, T> { +impl<'d, T: Instance> Timer<'d, T, Awaitable> { pub fn new( timer: impl Unborrow + 'd, irq: impl Unborrow + 'd, @@ -101,11 +117,18 @@ impl<'d, T: Instance> Timer<'d, T> { Self::new_irqless(timer) } +} +impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { + 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 `Uarte` internally. - pub(crate) fn new_irqless(_timer: impl Unborrow + 'd) -> Self { + fn new_irqless(_timer: impl Unborrow + 'd) -> Self { let regs = T::regs(); let mut this = Self { @@ -208,7 +231,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.", @@ -219,6 +242,7 @@ impl<'d, T: Instance> Timer<'d, T> { Cc { n, phantom: PhantomData, + phantom2: PhantomData, } } } @@ -230,12 +254,16 @@ 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> { n: usize, phantom: PhantomData<&'a mut T>, + phantom2: PhantomData, } -impl<'a, T: Instance> Cc<'a, T> { +impl<'a, T: Instance> Cc<'a, T, Awaitable> {} +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() diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index b2b298661..d164ebcbc 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, NotAwaitableTimer}; // 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}; @@ -289,7 +288,7 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { /// allowing it to implement the ReadUntilIdle trait. pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { uarte: Uarte<'d, U>, - timer: Timer<'d, T>, + timer: NotAwaitableTimer<'d, T>, ppi_ch1: Ppi<'d, AnyConfigurableChannel>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, } @@ -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 = NotAwaitableTimer::new(timer); unborrow!(ppi_ch1, ppi_ch2); From 1cef7134d42e581c86102df733f1420d86b20861 Mon Sep 17 00:00:00 2001 From: f_punk Date: Wed, 1 Sep 2021 16:20:32 +0200 Subject: [PATCH 2/3] moved CC::wait to awaitable typestate --- embassy-nrf/src/timer.rs | 68 ++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 939e99ed5..14a2f7b35 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -260,7 +260,40 @@ pub struct Cc<'a, T: Instance, I: TimerType> { phantom2: PhantomData, } -impl<'a, T: Instance> Cc<'a, T, Awaitable> {} +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> { @@ -332,37 +365,4 @@ impl<'a, T: Instance, I: TimerType> Cc<'a, T, I> { .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(); - } } From 34c66fa78d700fa5ca324dd043dc0861694ea693 Mon Sep 17 00:00:00 2001 From: f_punk Date: Thu, 2 Sep 2021 12:02:31 +0200 Subject: [PATCH 3/3] removed type aliases NotAwaitable as default generic param added awaitable_timer example --- embassy-nrf/src/buffered_uarte.rs | 6 ++--- embassy-nrf/src/timer.rs | 19 ++++++++-------- embassy-nrf/src/uarte.rs | 6 ++--- examples/nrf/src/bin/awaitable_timer.rs | 29 +++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 examples/nrf/src/bin/awaitable_timer.rs diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 5c9f4270b..90ce49582 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -16,7 +16,7 @@ use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; use crate::pac; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::Instance as TimerInstance; -use crate::timer::{Frequency, NotAwaitableTimer}; +use crate::timer::{Frequency, Timer}; use crate::uarte::{Config, Instance as UarteInstance}; // Re-export SVD variants to allow user to directly set values @@ -43,7 +43,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> { struct StateInner<'d, U: UarteInstance, T: TimerInstance> { phantom: PhantomData<&'d mut U>, - timer: NotAwaitableTimer<'d, T>, + timer: Timer<'d, T>, _ppi_ch1: Ppi<'d, AnyConfigurableChannel>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, @@ -84,7 +84,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { let r = U::regs(); - let mut timer = NotAwaitableTimer::new(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 14a2f7b35..638fd8229 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -97,15 +97,12 @@ impl sealed::TimerType for NotAwaitable {} impl TimerType for Awaitable {} impl TimerType for NotAwaitable {} -pub type AwaitableTimer<'d, T> = Timer<'d, T, Awaitable>; -pub type NotAwaitableTimer<'d, T> = Timer<'d, T, NotAwaitable>; - -pub struct Timer<'d, T: Instance, I: TimerType> { +pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { phantom: PhantomData<(&'d mut T, I)>, } impl<'d, T: Instance> Timer<'d, T, Awaitable> { - pub fn new( + pub fn new_awaitable( timer: impl Unborrow + 'd, irq: impl Unborrow + 'd, ) -> Self { @@ -119,6 +116,10 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> { } } impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { + /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. + /// + /// 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) } @@ -127,7 +128,7 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { 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 `Uarte` internally. + /// This is used by the public constructors. fn new_irqless(_timer: impl Unborrow + 'd) -> Self { let regs = T::regs(); @@ -242,7 +243,6 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { Cc { n, phantom: PhantomData, - phantom2: PhantomData, } } } @@ -254,10 +254,9 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// /// 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, I: TimerType> { +pub struct Cc<'a, T: Instance, I: TimerType = NotAwaitable> { n: usize, - phantom: PhantomData<&'a mut T>, - phantom2: PhantomData, + phantom: PhantomData<(&'a mut T, I)>, } impl<'a, T: Instance> Cc<'a, T, Awaitable> { diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index d164ebcbc..a6909be68 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -19,7 +19,7 @@ use crate::interrupt::Interrupt; use crate::pac; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::Instance as TimerInstance; -use crate::timer::{Frequency, NotAwaitableTimer}; +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}; @@ -288,7 +288,7 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { /// allowing it to implement the ReadUntilIdle trait. pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { uarte: Uarte<'d, U>, - timer: NotAwaitableTimer<'d, T>, + timer: Timer<'d, T>, ppi_ch1: Ppi<'d, AnyConfigurableChannel>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, } @@ -317,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 = NotAwaitableTimer::new(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"); + } +}