diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-09-02 13:39:55 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-09-02 13:39:55 +0200 |
| commit | d0c87493996cd5a80ad02533a888eef47fb94ba3 (patch) | |
| tree | d7d1fd8c291862c567a1536435483e93934e45b7 | |
| parent | db3cb02032fd6b861b2c39a0a354767cc72af1df (diff) | |
| parent | 34c66fa78d700fa5ca324dd043dc0861694ea693 (diff) | |
Merge pull request #382 from fnafnio/typestate_nrf_timer
Typestate nrf timer
| -rw-r--r-- | embassy-nrf/src/buffered_uarte.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/timer.rs | 113 | ||||
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 5 | ||||
| -rw-r--r-- | examples/nrf/src/bin/awaitable_timer.rs | 29 |
4 files changed, 103 insertions, 49 deletions
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 _; | |||
| 15 | use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; | 15 | use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; |
| 16 | use crate::pac; | 16 | use crate::pac; |
| 17 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; | 17 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; |
| 18 | use crate::timer::Frequency; | ||
| 19 | use crate::timer::Instance as TimerInstance; | 18 | use crate::timer::Instance as TimerInstance; |
| 20 | use crate::timer::Timer; | 19 | use crate::timer::{Frequency, Timer}; |
| 21 | use crate::uarte::{Config, Instance as UarteInstance}; | 20 | use crate::uarte::{Config, Instance as UarteInstance}; |
| 22 | 21 | ||
| 23 | // Re-export SVD variants to allow user to directly set values | 22 | // 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> { | |||
| 85 | 84 | ||
| 86 | let r = U::regs(); | 85 | let r = U::regs(); |
| 87 | 86 | ||
| 88 | let mut timer = Timer::new_irqless(timer); | 87 | let mut timer = Timer::new(timer); |
| 89 | 88 | ||
| 90 | rxd.conf().write(|w| w.input().connect().drive().h0h1()); | 89 | rxd.conf().write(|w| w.input().connect().drive().h0h1()); |
| 91 | r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); | 90 | 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 { | |||
| 27 | fn waker(n: usize) -> &'static AtomicWaker; | 27 | fn waker(n: usize) -> &'static AtomicWaker; |
| 28 | } | 28 | } |
| 29 | pub trait ExtendedInstance {} | 29 | pub trait ExtendedInstance {} |
| 30 | |||
| 31 | pub trait TimerType {} | ||
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | pub trait Instance: Unborrow<Target = Self> + sealed::Instance + 'static + Send { | 34 | pub trait Instance: Unborrow<Target = Self> + sealed::Instance + 'static + Send { |
| @@ -84,12 +86,23 @@ pub enum Frequency { | |||
| 84 | /// | 86 | /// |
| 85 | /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter | 87 | /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter |
| 86 | /// or trigger an event when the counter reaches a certain value. | 88 | /// or trigger an event when the counter reaches a certain value. |
| 87 | pub struct Timer<'d, T: Instance> { | 89 | |
| 88 | phantom: PhantomData<&'d mut T>, | 90 | pub trait TimerType: sealed::TimerType {} |
| 91 | |||
| 92 | pub enum Awaitable {} | ||
| 93 | pub enum NotAwaitable {} | ||
| 94 | |||
| 95 | impl sealed::TimerType for Awaitable {} | ||
| 96 | impl sealed::TimerType for NotAwaitable {} | ||
| 97 | impl TimerType for Awaitable {} | ||
| 98 | impl TimerType for NotAwaitable {} | ||
| 99 | |||
| 100 | pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { | ||
| 101 | phantom: PhantomData<(&'d mut T, I)>, | ||
| 89 | } | 102 | } |
| 90 | 103 | ||
| 91 | impl<'d, T: Instance> Timer<'d, T> { | 104 | impl<'d, T: Instance> Timer<'d, T, Awaitable> { |
| 92 | pub fn new( | 105 | pub fn new_awaitable( |
| 93 | timer: impl Unborrow<Target = T> + 'd, | 106 | timer: impl Unborrow<Target = T> + 'd, |
| 94 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | 107 | irq: impl Unborrow<Target = T::Interrupt> + 'd, |
| 95 | ) -> Self { | 108 | ) -> Self { |
| @@ -101,11 +114,22 @@ impl<'d, T: Instance> Timer<'d, T> { | |||
| 101 | 114 | ||
| 102 | Self::new_irqless(timer) | 115 | Self::new_irqless(timer) |
| 103 | } | 116 | } |
| 117 | } | ||
| 118 | impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { | ||
| 119 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. | ||
| 120 | /// | ||
| 121 | /// This can be useful for triggering tasks via PPI | ||
| 122 | /// `Uarte` uses this internally. | ||
| 123 | pub fn new(timer: impl Unborrow<Target = T> + 'd) -> Self { | ||
| 124 | Self::new_irqless(timer) | ||
| 125 | } | ||
| 126 | } | ||
| 104 | 127 | ||
| 128 | impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { | ||
| 105 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. | 129 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. |
| 106 | /// | 130 | /// |
| 107 | /// This is used by `Uarte` internally. | 131 | /// This is used by the public constructors. |
| 108 | pub(crate) fn new_irqless(_timer: impl Unborrow<Target = T> + 'd) -> Self { | 132 | fn new_irqless(_timer: impl Unborrow<Target = T> + 'd) -> Self { |
| 109 | let regs = T::regs(); | 133 | let regs = T::regs(); |
| 110 | 134 | ||
| 111 | let mut this = Self { | 135 | let mut this = Self { |
| @@ -208,7 +232,7 @@ impl<'d, T: Instance> Timer<'d, T> { | |||
| 208 | /// | 232 | /// |
| 209 | /// # Panics | 233 | /// # Panics |
| 210 | /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). | 234 | /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). |
| 211 | pub fn cc(&mut self, n: usize) -> Cc<T> { | 235 | pub fn cc(&mut self, n: usize) -> Cc<T, I> { |
| 212 | if n >= T::CCS { | 236 | if n >= T::CCS { |
| 213 | panic!( | 237 | panic!( |
| 214 | "Cannot get CC register {} of timer with {} CC registers.", | 238 | "Cannot get CC register {} of timer with {} CC registers.", |
| @@ -230,12 +254,48 @@ impl<'d, T: Instance> Timer<'d, T> { | |||
| 230 | /// | 254 | /// |
| 231 | /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. | 255 | /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. |
| 232 | /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register | 256 | /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register |
| 233 | pub struct Cc<'a, T: Instance> { | 257 | pub struct Cc<'a, T: Instance, I: TimerType = NotAwaitable> { |
| 234 | n: usize, | 258 | n: usize, |
| 235 | phantom: PhantomData<&'a mut T>, | 259 | phantom: PhantomData<(&'a mut T, I)>, |
| 260 | } | ||
| 261 | |||
| 262 | impl<'a, T: Instance> Cc<'a, T, Awaitable> { | ||
| 263 | /// Wait until the timer's counter reaches the value stored in this register. | ||
| 264 | /// | ||
| 265 | /// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`. | ||
| 266 | pub async fn wait(&mut self) { | ||
| 267 | let regs = T::regs(); | ||
| 268 | |||
| 269 | // Enable the interrupt for this CC's COMPARE event. | ||
| 270 | regs.intenset | ||
| 271 | .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); | ||
| 272 | |||
| 273 | // Disable the interrupt if the future is dropped. | ||
| 274 | let on_drop = OnDrop::new(|| { | ||
| 275 | regs.intenclr | ||
| 276 | .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); | ||
| 277 | }); | ||
| 278 | |||
| 279 | poll_fn(|cx| { | ||
| 280 | T::waker(self.n).register(cx.waker()); | ||
| 281 | |||
| 282 | if regs.events_compare[self.n].read().bits() != 0 { | ||
| 283 | // Reset the register for next time | ||
| 284 | regs.events_compare[self.n].reset(); | ||
| 285 | Poll::Ready(()) | ||
| 286 | } else { | ||
| 287 | Poll::Pending | ||
| 288 | } | ||
| 289 | }) | ||
| 290 | .await; | ||
| 291 | |||
| 292 | // The interrupt was already disabled in the interrupt handler, so there's no need to disable it again. | ||
| 293 | on_drop.defuse(); | ||
| 294 | } | ||
| 236 | } | 295 | } |
| 296 | impl<'a, T: Instance> Cc<'a, T, NotAwaitable> {} | ||
| 237 | 297 | ||
| 238 | impl<'a, T: Instance> Cc<'a, T> { | 298 | impl<'a, T: Instance, I: TimerType> Cc<'a, T, I> { |
| 239 | /// Get the current value stored in the register. | 299 | /// Get the current value stored in the register. |
| 240 | pub fn read(&self) -> u32 { | 300 | pub fn read(&self) -> u32 { |
| 241 | T::regs().cc[self.n].read().cc().bits() | 301 | T::regs().cc[self.n].read().cc().bits() |
| @@ -304,37 +364,4 @@ impl<'a, T: Instance> Cc<'a, T> { | |||
| 304 | .shorts | 364 | .shorts |
| 305 | .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (8 + self.n))) }) | 365 | .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (8 + self.n))) }) |
| 306 | } | 366 | } |
| 307 | |||
| 308 | /// Wait until the timer's counter reaches the value stored in this register. | ||
| 309 | /// | ||
| 310 | /// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`. | ||
| 311 | pub async fn wait(&mut self) { | ||
| 312 | let regs = T::regs(); | ||
| 313 | |||
| 314 | // Enable the interrupt for this CC's COMPARE event. | ||
| 315 | regs.intenset | ||
| 316 | .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); | ||
| 317 | |||
| 318 | // Disable the interrupt if the future is dropped. | ||
| 319 | let on_drop = OnDrop::new(|| { | ||
| 320 | regs.intenclr | ||
| 321 | .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); | ||
| 322 | }); | ||
| 323 | |||
| 324 | poll_fn(|cx| { | ||
| 325 | T::waker(self.n).register(cx.waker()); | ||
| 326 | |||
| 327 | if regs.events_compare[self.n].read().bits() != 0 { | ||
| 328 | // Reset the register for next time | ||
| 329 | regs.events_compare[self.n].reset(); | ||
| 330 | Poll::Ready(()) | ||
| 331 | } else { | ||
| 332 | Poll::Pending | ||
| 333 | } | ||
| 334 | }) | ||
| 335 | .await; | ||
| 336 | |||
| 337 | // The interrupt was already disabled in the interrupt handler, so there's no need to disable it again. | ||
| 338 | on_drop.defuse(); | ||
| 339 | } | ||
| 340 | } | 367 | } |
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}; | |||
| 18 | use crate::interrupt::Interrupt; | 18 | use crate::interrupt::Interrupt; |
| 19 | use crate::pac; | 19 | use crate::pac; |
| 20 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; | 20 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; |
| 21 | use crate::timer::Frequency; | ||
| 22 | use crate::timer::Instance as TimerInstance; | 21 | use crate::timer::Instance as TimerInstance; |
| 23 | use crate::timer::Timer; | 22 | use crate::timer::{Frequency, Timer}; |
| 24 | 23 | ||
| 25 | // Re-export SVD variants to allow user to directly set values. | 24 | // Re-export SVD variants to allow user to directly set values. |
| 26 | pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; | 25 | 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> { | |||
| 318 | ) -> Self { | 317 | ) -> Self { |
| 319 | let baudrate = config.baudrate; | 318 | let baudrate = config.baudrate; |
| 320 | let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config); | 319 | let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config); |
| 321 | let mut timer = Timer::new_irqless(timer); | 320 | let mut timer = Timer::new(timer); |
| 322 | 321 | ||
| 323 | unborrow!(ppi_ch1, ppi_ch2); | 322 | unborrow!(ppi_ch1, ppi_ch2); |
| 324 | 323 | ||
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | #![allow(incomplete_features)] | ||
| 5 | |||
| 6 | #[path = "../example_common.rs"] | ||
| 7 | mod example_common; | ||
| 8 | use embassy_nrf::interrupt; | ||
| 9 | use embassy_nrf::timer::Timer; | ||
| 10 | use embassy_nrf::Peripherals; | ||
| 11 | use example_common::info; | ||
| 12 | |||
| 13 | use embassy::executor::Spawner; | ||
| 14 | |||
| 15 | #[embassy::main] | ||
| 16 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 17 | let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); | ||
| 18 | // default frequency is 1MHz, so this triggers every second | ||
| 19 | t.cc(0).write(1_000_000); | ||
| 20 | // clear the timer value on cc[0] compare match | ||
| 21 | t.cc(0).short_compare_clear(); | ||
| 22 | t.start(); | ||
| 23 | |||
| 24 | loop { | ||
| 25 | // wait for compare match | ||
| 26 | t.cc(0).wait().await; | ||
| 27 | info!("hardware timer tick"); | ||
| 28 | } | ||
| 29 | } | ||
