From a054891263cbd17315cdf85ecdcc6359313063bc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 21 Feb 2023 22:19:06 +0100 Subject: [PATCH 01/17] cortex-m: rename Handler to DynHandler. I want to use the name Handler for the new interrupt binding macro. --- embassy-cortex-m/src/interrupt.rs | 6 +++--- embassy-macros/src/macros/cortex_m_interrupt_declare.rs | 4 ++-- embassy-macros/src/macros/cortex_m_interrupt_take.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 1df8671b..ead9d52f 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -15,12 +15,12 @@ pub mod _export { /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] -pub struct Handler { +pub struct DynHandler { pub func: AtomicPtr<()>, pub ctx: AtomicPtr<()>, } -impl Handler { +impl DynHandler { pub const fn new() -> Self { Self { func: AtomicPtr::new(ptr::null_mut()), @@ -51,7 +51,7 @@ pub unsafe trait Interrupt: Peripheral

{ /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] - unsafe fn __handler(&self) -> &'static Handler; + unsafe fn __handler(&self) -> &'static DynHandler; } /// Represents additional behavior for all interrupts. diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs index ebbb47cd..699883ef 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs @@ -21,9 +21,9 @@ pub fn run(name: syn::Ident) -> Result { unsafe fn steal() -> Self { Self(()) } - unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::Handler { + unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::DynHandler { #[export_name = #name_handler] - static HANDLER: ::embassy_cortex_m::interrupt::Handler = ::embassy_cortex_m::interrupt::Handler::new(); + static HANDLER: ::embassy_cortex_m::interrupt::DynHandler = ::embassy_cortex_m::interrupt::DynHandler::new(); &HANDLER } } diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index d30189ce..e2ebf98c 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -30,7 +30,7 @@ pub fn run(name: syn::Ident) -> Result { pub unsafe extern "C" fn trampoline() { extern "C" { #[link_name = #name_handler] - static HANDLER: interrupt::Handler; + static HANDLER: interrupt::DynHandler; } let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); From 42c13c8c3d9514866c2842009f76e88e8cb01b22 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Feb 2023 22:42:22 +0100 Subject: [PATCH 02/17] nrf: add new interrupt binding traits and macro. --- embassy-cortex-m/src/interrupt.rs | 30 ++++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 33 +++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index ead9d52f..3a82726d 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -13,6 +13,36 @@ pub mod _export { pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; } +/// Interrupt handler trait. +/// +/// Drivers that need to handle interrupts implement this trait. +/// The user must ensure `on_interrupt()` is called every time the interrupt fires. +/// Drivers must use use [`Binding`] to assert at compile time that the user has done so. +pub trait Handler { + /// Interrupt handler function. + /// + /// Must be called every time the `I` interrupt fires, synchronously from + /// the interrupt handler context. + /// + /// # Safety + /// + /// This function must ONLY be called from the interrupt handler for `I`. + unsafe fn on_interrupt(); +} + +/// Compile-time assertion that an interrupt has been bound to a handler. +/// +/// For the vast majority of cases, you should use the `bind_interrupts!` +/// macro instead of writing `unsafe impl`s of this trait. +/// +/// # Safety +/// +/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` +/// to be called every time the `I` interrupt fires. +/// +/// This allows drivers to check bindings at compile-time. +pub unsafe trait Binding> {} + /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] pub struct DynHandler { diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 3c5db5c7..446b1f41 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -95,14 +95,39 @@ pub mod wdt; #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] mod chip; -pub use chip::EASY_DMA_SIZE; - pub mod interrupt { - //! nRF interrupts for cortex-m devices. + //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; pub use embassy_cortex_m::interrupt::*; pub use crate::chip::irqs::*; + + /// Macro to bind interrupts to handlers. + /// + /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) + /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to + /// prove at compile-time that the right interrupts have been bound. + // developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. + #[macro_export] + macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} + )* + )* + }; + } } // Reexports @@ -111,7 +136,7 @@ pub mod interrupt { pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; -pub use chip::{peripherals, Peripherals}; +pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use embassy_cortex_m::executor; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; From 9cf000ef4edd8f230b348ede8d7ce015045a0035 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:17:52 +0100 Subject: [PATCH 03/17] nrf/uart: switch to new interrupt binding. --- embassy-nrf/src/buffered_uarte.rs | 411 +++++++++++---------- embassy-nrf/src/uarte.rs | 85 ++--- examples/nrf52840/src/bin/buffered_uart.rs | 11 +- examples/nrf52840/src/bin/uart.rs | 9 +- examples/nrf52840/src/bin/uart_idle.rs | 10 +- examples/nrf52840/src/bin/uart_split.rs | 9 +- examples/nrf5340/src/bin/uart.rs | 10 +- tests/nrf/src/bin/buffered_uart.rs | 10 +- tests/nrf/src/bin/buffered_uart_spam.rs | 13 +- 9 files changed, 299 insertions(+), 269 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index ab639aee..75f93f90 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -10,6 +10,7 @@ use core::cmp::min; use core::future::poll_fn; +use core::marker::PhantomData; use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; @@ -23,7 +24,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::gpio::sealed::Pin; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::InterruptExt; +use crate::interrupt::{self, InterruptExt}; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; @@ -71,211 +72,13 @@ impl State { } } -/// Buffered UARTE driver. -pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - _peri: PeripheralRef<'d, U>, - timer: Timer<'d, T>, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_group: PpiGroup<'d, AnyGroup>, +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} - -impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { - /// Create a new BufferedUarte without hardware flow control. - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - ppi_group: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); - Self::new_inner( - uarte, - timer, - ppi_ch1.map_into(), - ppi_ch2.map_into(), - ppi_group.map_into(), - irq, - rxd.map_into(), - txd.map_into(), - None, - None, - config, - rx_buffer, - tx_buffer, - ) - } - - /// Create a new BufferedUarte with hardware flow control (RTS/CTS) - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new_with_rtscts( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - ppi_group: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - cts: impl Peripheral

+ 'd, - rts: impl Peripheral

+ 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); - Self::new_inner( - uarte, - timer, - ppi_ch1.map_into(), - ppi_ch2.map_into(), - ppi_group.map_into(), - irq, - rxd.map_into(), - txd.map_into(), - Some(cts.map_into()), - Some(rts.map_into()), - config, - rx_buffer, - tx_buffer, - ) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, - ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, - ppi_group: PeripheralRef<'d, AnyGroup>, - irq: impl Peripheral

+ 'd, - rxd: PeripheralRef<'d, AnyPin>, - txd: PeripheralRef<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(peri, timer, irq); - - assert!(rx_buffer.len() % 2 == 0); - - let r = U::regs(); - - rxd.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); - - txd.set_high(); - txd.conf().write(|w| w.dir().output().drive().h0h1()); - r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - - if let Some(pin) = &cts { - pin.conf().write(|w| w.input().connect().drive().h0h1()); - } - r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); - - if let Some(pin) = &rts { - pin.set_high(); - pin.conf().write(|w| w.dir().output().drive().h0h1()); - } - r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - - // Initialize state - let s = U::buffered_state(); - s.tx_count.store(0, Ordering::Relaxed); - s.rx_bufs.store(0, Ordering::Relaxed); - let len = tx_buffer.len(); - unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let len = rx_buffer.len(); - unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - - // Configure - r.config.write(|w| { - w.hwfc().bit(false); - w.parity().variant(config.parity); - w - }); - r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - - // clear errors - let errors = r.errorsrc.read().bits(); - r.errorsrc.write(|w| unsafe { w.bits(errors) }); - - r.events_rxstarted.reset(); - r.events_txstarted.reset(); - r.events_error.reset(); - r.events_endrx.reset(); - r.events_endtx.reset(); - - // Enable interrupts - r.intenclr.write(|w| unsafe { w.bits(!0) }); - r.intenset.write(|w| { - w.endtx().set(); - w.rxstarted().set(); - w.error().set(); - w - }); - - // Enable UARTE instance - apply_workaround_for_enable_anomaly(&r); - r.enable.write(|w| w.enable().enabled()); - - // Configure byte counter. - let mut timer = Timer::new_counter(timer); - timer.cc(1).write(rx_buffer.len() as u32 * 2); - timer.cc(1).short_compare_clear(); - timer.clear(); - timer.start(); - - let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); - ppi_ch1.enable(); - - s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); - let mut ppi_group = PpiGroup::new(ppi_group); - let mut ppi_ch2 = Ppi::new_one_to_two( - ppi_ch2, - Event::from_reg(&r.events_endrx), - Task::from_reg(&r.tasks_startrx), - ppi_group.task_disable_all(), - ); - ppi_ch2.disable(); - ppi_group.add_channel(&ppi_ch2); - - irq.disable(); - irq.set_handler(Self::on_interrupt); - irq.pend(); - irq.enable(); - - Self { - _peri: peri, - timer, - _ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - _ppi_group: ppi_group, - } - } - - fn pend_irq() { - unsafe { ::steal() }.pend() - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { //trace!("irq: start"); let r = U::regs(); let s = U::buffered_state(); @@ -374,6 +177,206 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { //trace!("irq: end"); } +} + +/// Buffered UARTE driver. +pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { + _peri: PeripheralRef<'d, U>, + timer: Timer<'d, T>, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_group: PpiGroup<'d, AnyGroup>, +} + +impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} + +impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { + /// Create a new BufferedUarte without hardware flow control. + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + rxd.map_into(), + txd.map_into(), + None, + None, + config, + rx_buffer, + tx_buffer, + ) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new_with_rtscts( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + cts: impl Peripheral

+ 'd, + rts: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + rxd.map_into(), + txd.map_into(), + Some(cts.map_into()), + Some(rts.map_into()), + config, + rx_buffer, + tx_buffer, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_group: PeripheralRef<'d, AnyGroup>, + rxd: PeripheralRef<'d, AnyPin>, + txd: PeripheralRef<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(peri, timer); + + assert!(rx_buffer.len() % 2 == 0); + + let r = U::regs(); + + rxd.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); + + txd.set_high(); + txd.conf().write(|w| w.dir().output().drive().h0h1()); + r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); + + if let Some(pin) = &cts { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } + r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + + if let Some(pin) = &rts { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } + r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + + // Initialize state + let s = U::buffered_state(); + s.tx_count.store(0, Ordering::Relaxed); + s.rx_bufs.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + // Configure + r.config.write(|w| { + w.hwfc().bit(false); + w.parity().variant(config.parity); + w + }); + r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); + + // clear errors + let errors = r.errorsrc.read().bits(); + r.errorsrc.write(|w| unsafe { w.bits(errors) }); + + r.events_rxstarted.reset(); + r.events_txstarted.reset(); + r.events_error.reset(); + r.events_endrx.reset(); + r.events_endtx.reset(); + + // Enable interrupts + r.intenclr.write(|w| unsafe { w.bits(!0) }); + r.intenset.write(|w| { + w.endtx().set(); + w.rxstarted().set(); + w.error().set(); + w + }); + + // Enable UARTE instance + apply_workaround_for_enable_anomaly(&r); + r.enable.write(|w| w.enable().enabled()); + + // Configure byte counter. + let mut timer = Timer::new_counter(timer); + timer.cc(1).write(rx_buffer.len() as u32 * 2); + timer.cc(1).short_compare_clear(); + timer.clear(); + timer.start(); + + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); + ppi_ch1.enable(); + + s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); + let mut ppi_group = PpiGroup::new(ppi_group); + let mut ppi_ch2 = Ppi::new_one_to_two( + ppi_ch2, + Event::from_reg(&r.events_endrx), + Task::from_reg(&r.tasks_startrx), + ppi_group.task_disable_all(), + ); + ppi_ch2.disable(); + ppi_group.add_channel(&ppi_ch2); + + unsafe { U::Interrupt::steal() }.pend(); + unsafe { U::Interrupt::steal() }.enable(); + + Self { + _peri: peri, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + _ppi_group: ppi_group, + } + } + + fn pend_irq() { + unsafe { ::steal() }.pend() + } /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 00afbd05..3934d1b5 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -14,6 +14,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -26,7 +27,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; @@ -62,6 +63,27 @@ pub enum Error { BufferNotInRAM, } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_endrx.read().bits() != 0 { + s.endrx_waker.wake(); + r.intenclr.write(|w| w.endrx().clear()); + } + if r.events_endtx.read().bits() != 0 { + s.endtx_waker.wake(); + r.intenclr.write(|w| w.endtx().clear()); + } + } +} + /// UARTE driver. pub struct Uarte<'d, T: Instance> { tx: UarteTx<'d, T>, @@ -86,19 +108,19 @@ impl<'d, T: Instance> Uarte<'d, T> { /// Create a new UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd, txd); - Self::new_inner(uarte, irq, rxd.map_into(), txd.map_into(), None, None, config) + Self::new_inner(uarte, rxd.map_into(), txd.map_into(), None, None, config) } /// Create a new UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, @@ -108,7 +130,6 @@ impl<'d, T: Instance> Uarte<'d, T> { into_ref!(rxd, txd, cts, rts); Self::new_inner( uarte, - irq, rxd.map_into(), txd.map_into(), Some(cts.map_into()), @@ -119,14 +140,13 @@ impl<'d, T: Instance> Uarte<'d, T> { fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rxd: PeripheralRef<'d, AnyPin>, txd: PeripheralRef<'d, AnyPin>, cts: Option>, rts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -148,9 +168,8 @@ impl<'d, T: Instance> Uarte<'d, T> { } r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let hardware_flow_control = match (rts.is_some(), cts.is_some()) { (false, false) => false, @@ -238,20 +257,6 @@ impl<'d, T: Instance> Uarte<'d, T> { Event::from_reg(&r.events_endtx) } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_endrx.read().bits() != 0 { - s.endrx_waker.wake(); - r.intenclr.write(|w| w.endrx().clear()); - } - if r.events_endtx.read().bits() != 0 { - s.endtx_waker.wake(); - r.intenclr.write(|w| w.endtx().clear()); - } - } - /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.read(buffer).await @@ -308,34 +313,33 @@ impl<'d, T: Instance> UarteTx<'d, T> { /// Create a new tx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(txd); - Self::new_inner(uarte, irq, txd.map_into(), None, config) + Self::new_inner(uarte, txd.map_into(), None, config) } /// Create a new tx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(txd, cts); - Self::new_inner(uarte, irq, txd.map_into(), Some(cts.map_into()), config) + Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config) } fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, txd: PeripheralRef<'d, AnyPin>, cts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -354,9 +358,8 @@ impl<'d, T: Instance> UarteTx<'d, T> { let hardware_flow_control = cts.is_some(); configure(r, config, hardware_flow_control); - irq.set_handler(Uarte::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); @@ -506,34 +509,33 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Create a new rx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd); - Self::new_inner(uarte, irq, rxd.map_into(), None, config) + Self::new_inner(uarte, rxd.map_into(), None, config) } /// Create a new rx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, rts: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd, rts); - Self::new_inner(uarte, irq, rxd.map_into(), Some(rts.map_into()), config) + Self::new_inner(uarte, rxd.map_into(), Some(rts.map_into()), config) } fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rxd: PeripheralRef<'d, AnyPin>, rts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -549,9 +551,8 @@ impl<'d, T: Instance> UarteRx<'d, T> { r.psel.txd.write(|w| w.connect().disconnected()); r.psel.cts.write(|w| w.connect().disconnected()); - irq.set_handler(Uarte::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let hardware_flow_control = rts.is_some(); configure(r, config, hardware_flow_control); diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 5b934b7d..23869537 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -4,11 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::BufferedUarte; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use embedded_io::asynch::Write; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -19,14 +23,13 @@ async fn main(_spawner: Spawner) { let mut tx_buffer = [0u8; 4096]; let mut rx_buffer = [0u8; 4096]; - let irq = interrupt::take!(UARTE0_UART0); let mut u = BufferedUarte::new( p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - irq, + Irqs, p.P0_08, p.P0_06, config, diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index 600f7a6e..50d5cab8 100644 --- a/examples/nrf52840/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs @@ -4,9 +4,13 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +18,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let mut uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let mut uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); info!("uarte initialized!"); diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index 6af4f709..e1f42fa6 100644 --- a/examples/nrf52840/src/bin/uart_idle.rs +++ b/examples/nrf52840/src/bin/uart_idle.rs @@ -4,9 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::peripherals::UARTE0; +use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); info!("uarte initialized!"); diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 1adaf53f..9979a1d5 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -6,13 +6,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::peripherals::UARTE0; use embassy_nrf::uarte::UarteRx; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::{bind_interrupts, uarte}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; static CHANNEL: Channel = Channel::new(); +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -20,8 +24,7 @@ async fn main(spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); let (mut tx, rx) = uart.split(); info!("uarte initialized!"); diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 5f448c2b..d6853970 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs @@ -4,9 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::peripherals::SERIAL0; +use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SERIAL0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(SERIAL0); - let mut uart = uarte::Uarte::new(p.SERIAL0, irq, p.P1_00, p.P1_01, config); + let mut uart = uarte::Uarte::new(p.SERIAL0, Irqs, p.P1_00, p.P1_01, config); info!("uarte initialized!"); diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index 0550b0bb..e73d4f0b 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -5,10 +5,14 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::buffered_uarte::BufferedUarte; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -25,7 +29,7 @@ async fn main(_spawner: Spawner) { p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - interrupt::take!(UARTE0_UART0), + Irqs, p.P1_03, p.P1_02, config.clone(), diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 57aaeca4..74eda6d0 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -7,14 +7,19 @@ use core::ptr::NonNull; use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::ppi::{Event, Ppi, Task}; use embassy_nrf::uarte::Uarte; -use embassy_nrf::{interrupt, pac, uarte}; +use embassy_nrf::{bind_interrupts, pac, peripherals, uarte}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; + UARTE1 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut p = embassy_nrf::init(Default::default()); @@ -33,7 +38,7 @@ async fn main(_spawner: Spawner) { p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - interrupt::take!(UARTE0_UART0), + Irqs, p.P1_03, p.P1_04, config.clone(), @@ -49,7 +54,7 @@ async fn main(_spawner: Spawner) { // Tx spam in a loop. const NSPAM: usize = 17; static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone()); + let _spam = Uarte::new(p.UARTE1, Irqs, p.P1_01, p.P1_02, config.clone()); let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) }; let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) }; let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; From 63b75eaf644eee2da2c16f72dbd46bb404f5fdbd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:27:33 +0100 Subject: [PATCH 04/17] nrf/timer: remove awaitable. --- embassy-nrf/src/timer.rs | 134 ++----------------- examples/nrf52840/src/bin/awaitable_timer.rs | 26 ---- 2 files changed, 10 insertions(+), 150 deletions(-) delete mode 100644 examples/nrf52840/src/bin/awaitable_timer.rs diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3b0d2f1c..a9487a9f 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,15 +6,9 @@ #![macro_use] -use core::future::poll_fn; -use core::marker::PhantomData; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; @@ -26,8 +20,6 @@ pub(crate) mod sealed { /// The number of CC registers this instance has. const CCS: usize; fn regs() -> &'static pac::timer0::RegisterBlock; - /// Storage for the waker for CC register `n`. - fn waker(n: usize) -> &'static AtomicWaker; } pub trait ExtendedInstance {} @@ -50,12 +42,6 @@ macro_rules! impl_timer { fn regs() -> &'static pac::timer0::RegisterBlock { unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } } - fn waker(n: usize) -> &'static ::embassy_sync::waitqueue::AtomicWaker { - use ::embassy_sync::waitqueue::AtomicWaker; - const NEW_AW: AtomicWaker = AtomicWaker::new(); - static WAKERS: [AtomicWaker; $ccs] = [NEW_AW; $ccs]; - &WAKERS[n] - } } impl crate::timer::Instance for peripherals::$type { type Interrupt = crate::interrupt::$irq; @@ -99,59 +85,18 @@ pub enum Frequency { /// nRF Timer driver. /// /// The timer has an internal counter, which is incremented for every tick of the timer. -/// The counter is 32-bit, so it wraps back to 0 at 4294967296. +/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32. /// /// 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 trait TimerType: sealed::TimerType {} - -/// Marker type indicating the timer driver can await expiration (it owns the timer interrupt). -pub enum Awaitable {} - -/// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt). -pub enum NotAwaitable {} - -impl sealed::TimerType for Awaitable {} -impl sealed::TimerType for NotAwaitable {} -impl TimerType for Awaitable {} -impl TimerType for NotAwaitable {} - /// Timer driver. -pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { +pub struct Timer<'d, T: Instance> { _p: PeripheralRef<'d, T>, - _i: PhantomData, } -impl<'d, T: Instance> Timer<'d, T, Awaitable> { - /// Create a new async-capable timer driver. - pub fn new_awaitable(timer: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner(timer, false) - } - - /// Create a new async-capable timer driver in counter mode. - pub fn new_awaitable_counter( - timer: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - ) -> Self { - into_ref!(irq); - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner(timer, true) - } -} - -impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { - /// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work. +impl<'d, T: Instance> Timer<'d, T> { + /// Create a new `Timer` driver. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. @@ -159,28 +104,20 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { Self::new_inner(timer, false) } - /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work. + /// Create a new `Timer` driver in counter mode. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new_counter(timer: impl Peripheral

+ 'd) -> Self { Self::new_inner(timer, true) } -} -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_inner(timer: impl Peripheral

+ 'd, is_counter: bool) -> Self { into_ref!(timer); let regs = T::regs(); - let mut this = Self { - _p: timer, - _i: PhantomData, - }; + let mut this = Self { _p: timer }; // Stop the timer before doing anything else, // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. @@ -272,31 +209,17 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { .write(|w| unsafe { w.prescaler().bits(frequency as u8) }) } - fn on_interrupt(_: *mut ()) { - let regs = T::regs(); - for n in 0..T::CCS { - if regs.events_compare[n].read().bits() != 0 { - // Clear the interrupt, otherwise the interrupt will be repeatedly raised as soon as the interrupt handler exits. - // We can't clear the event, because it's used to poll whether the future is done or still pending. - regs.intenclr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + n))) }); - T::waker(n).wake(); - } - } - } - /// Returns this timer's `n`th CC register. /// /// # 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.", n, T::CCS); } Cc { n, _p: self._p.reborrow(), - _i: PhantomData, } } } @@ -308,49 +231,12 @@ 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<'d, T: Instance, I: TimerType = NotAwaitable> { +pub struct Cc<'d, T: Instance> { n: usize, _p: PeripheralRef<'d, T>, - _i: PhantomData, } -impl<'d, T: Instance> Cc<'d, 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<'d, T: Instance> Cc<'d, T, NotAwaitable> {} - -impl<'d, T: Instance, I: TimerType> Cc<'d, T, I> { +impl<'d, T: Instance> Cc<'d, T> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { T::regs().cc[self.n].read().cc().bits() diff --git a/examples/nrf52840/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs deleted file mode 100644 index b32af236..00000000 --- a/examples/nrf52840/src/bin/awaitable_timer.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::info; -use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::timer::Timer; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - 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"); - } -} From 34563b74aa48c53622344541153266b0227fc9bf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:40:13 +0100 Subject: [PATCH 05/17] nrf/i2s: switch to new interrupt binding. --- embassy-nrf/src/i2s.rs | 77 ++++++++++++----------- examples/nrf52840/src/bin/i2s_effect.rs | 15 +++-- examples/nrf52840/src/bin/i2s_monitor.rs | 9 ++- examples/nrf52840/src/bin/i2s_waveform.rs | 9 ++- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 770df7c8..8a1188ce 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,7 +14,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::Interrupt; +use crate::interrupt::{self, Interrupt}; use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; @@ -363,10 +363,39 @@ impl From for u8 { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let device = Device::::new(); + let s = T::state(); + + if device.is_tx_ptr_updated() { + trace!("TX INT"); + s.tx_waker.wake(); + device.disable_tx_ptr_interrupt(); + } + + if device.is_rx_ptr_updated() { + trace!("RX INT"); + s.rx_waker.wake(); + device.disable_rx_ptr_interrupt(); + } + + if device.is_stopped() { + trace!("STOPPED INT"); + s.stop_waker.wake(); + device.disable_stopped_interrupt(); + } + } +} + /// I2S driver. pub struct I2S<'d, T: Instance> { i2s: PeripheralRef<'d, T>, - irq: PeripheralRef<'d, T::Interrupt>, mck: Option>, sck: PeripheralRef<'d, AnyPin>, lrck: PeripheralRef<'d, AnyPin>, @@ -378,19 +407,18 @@ pub struct I2S<'d, T: Instance> { impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S in master mode - pub fn master( + pub fn new_master( i2s: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, mck: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, master_clock: MasterClock, config: Config, ) -> Self { - into_ref!(i2s, irq, mck, sck, lrck); + into_ref!(i2s, mck, sck, lrck); Self { i2s, - irq, mck: Some(mck.map_into()), sck: sck.map_into(), lrck: lrck.map_into(), @@ -402,17 +430,16 @@ impl<'d, T: Instance> I2S<'d, T> { } /// Create a new I2S in slave mode - pub fn slave( + pub fn new_slave( i2s: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(i2s, irq, sck, lrck); + into_ref!(i2s, sck, lrck); Self { i2s, - irq, mck: None, sck: sck.map_into(), lrck: lrck.map_into(), @@ -537,9 +564,8 @@ impl<'d, T: Instance> I2S<'d, T> { } fn setup_interrupt(&self) { - self.irq.set_handler(Self::on_interrupt); - self.irq.unpend(); - self.irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let device = Device::::new(); device.disable_tx_ptr_interrupt(); @@ -555,29 +581,6 @@ impl<'d, T: Instance> I2S<'d, T> { device.enable_stopped_interrupt(); } - fn on_interrupt(_: *mut ()) { - let device = Device::::new(); - let s = T::state(); - - if device.is_tx_ptr_updated() { - trace!("TX INT"); - s.tx_waker.wake(); - device.disable_tx_ptr_interrupt(); - } - - if device.is_rx_ptr_updated() { - trace!("RX INT"); - s.rx_waker.wake(); - device.disable_rx_ptr_interrupt(); - } - - if device.is_stopped() { - trace!("STOPPED INT"); - s.stop_waker.wake(); - device.disable_stopped_interrupt(); - } - } - async fn stop() { compiler_fence(Ordering::SeqCst); @@ -1168,7 +1171,7 @@ pub(crate) mod sealed { } } -/// I2S peripehral instance. +/// I2S peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: Interrupt; diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs index 52d46e4f..391514d9 100644 --- a/examples/nrf52840/src/bin/i2s_effect.rs +++ b/examples/nrf52840/src/bin/i2s_effect.rs @@ -7,7 +7,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; -use embassy_nrf::interrupt; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; @@ -15,6 +15,10 @@ type Sample = i16; const NUM_BUFFERS: usize = 2; const NUM_SAMPLES: usize = 4; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -28,15 +32,10 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.channels = Channels::MonoLeft; - let irq = interrupt::take!(I2S); let buffers_out = MultiBuffering::::new(); let buffers_in = MultiBuffering::::new(); - let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( - p.P0_29, - p.P0_28, - buffers_out, - buffers_in, - ); + let mut full_duplex_stream = I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config) + .full_duplex(p.P0_29, p.P0_28, buffers_out, buffers_in); let mut modulator = SineOsc::new(); modulator.set_frequency(8.0, 1.0 / sample_rate as f32); diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs index 5ebfd954..4ed597c0 100644 --- a/examples/nrf52840/src/bin/i2s_monitor.rs +++ b/examples/nrf52840/src/bin/i2s_monitor.rs @@ -5,14 +5,18 @@ use defmt::{debug, error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; -use embassy_nrf::interrupt; use embassy_nrf::pwm::{Prescaler, SimplePwm}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; const NUM_SAMPLES: usize = 500; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,10 +30,9 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.channels = Channels::MonoLeft; - let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); let mut input_stream = - I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); + I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); // Configure the PWM to use the pins corresponding to the RGB leds let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs index eda93067..f2c1166b 100644 --- a/examples/nrf52840/src/bin/i2s_waveform.rs +++ b/examples/nrf52840/src/bin/i2s_waveform.rs @@ -7,13 +7,17 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; -use embassy_nrf::interrupt; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; const NUM_SAMPLES: usize = 50; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -27,10 +31,9 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.channels = Channels::MonoLeft; - let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); let mut output_stream = - I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); + I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); let mut waveform = Waveform::new(1.0 / sample_rate as f32); From f8f1d3bcf09045616f5b63a08a9623cd14acd045 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:50:45 +0100 Subject: [PATCH 06/17] nrf/pdm: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 5 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_app.rs | 7 +- embassy-nrf/src/chips/nrf9160.rs | 2 + embassy-nrf/src/lib.rs | 2 + embassy-nrf/src/pdm.rs | 131 ++++++++++++++++++--------- examples/nrf52840/src/bin/pdm.rs | 10 +- 10 files changed, 116 insertions(+), 49 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 6b5c134b..c6ccb6a0 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -148,6 +148,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index c5de9a44..7d1bce1c 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -150,6 +150,8 @@ impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index c2b23fc5..ce19a18e 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -146,6 +146,9 @@ embassy_hal_common::peripherals! { // I2S I2S, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -168,6 +171,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 95f71ade..08b82021 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -197,6 +197,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 5e7479e8..4e8b6d9e 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -208,6 +208,8 @@ impl_timer!(TIMER4, TIMER4, TIMER4, extended); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM, PDM, PDM); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 9c7b738e..050612b1 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -34,7 +34,7 @@ pub mod pac { nvmc_ns as nvmc, oscillators_ns as oscillators, p0_ns as p0, - pdm0_ns as pdm0, + pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, qdec0_ns as qdec0, @@ -253,6 +253,9 @@ embassy_hal_common::peripherals! { // QSPI QSPI, + // PDM + PDM0, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -398,6 +401,8 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM0, PDM0, PDM0); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 385bd133..d2b45114 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -301,6 +301,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 446b1f41..17aa5ad1 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,8 +47,10 @@ pub mod nvmc; #[cfg(any( feature = "nrf52810", feature = "nrf52811", + feature = "nrf52832", feature = "nrf52833", feature = "nrf52840", + feature = "_nrf5340-app", feature = "_nrf9160" ))] pub mod pdm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 54feca4c..8815bb31 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,25 +1,37 @@ //! Pulse Density Modulation (PDM) mirophone driver. +#![macro_use] + use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::{self, InterruptExt}; -use crate::peripherals::PDM; -use crate::{pac, Peripheral}; +use crate::Peripheral; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.end().clear()); + T::state().waker.wake(); + } +} /// PDM microphone interface -pub struct Pdm<'d> { - irq: PeripheralRef<'d, interrupt::PDM>, - phantom: PhantomData<&'d PDM>, +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, } /// PDM error. @@ -35,32 +47,30 @@ pub enum Error { NotRunning, } -static WAKER: AtomicWaker = AtomicWaker::new(); static DUMMY_BUFFER: [i16; 1] = [0; 1]; -impl<'d> Pdm<'d> { +impl<'d, T: Instance> Pdm<'d, T> { /// Create PDM driver pub fn new( - pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, clk: impl Peripheral

+ 'd, din: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(clk, din); - Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config) + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) } fn new_inner( - _pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: PeripheralRef<'d, T>, clk: PeripheralRef<'d, AnyPin>, din: PeripheralRef<'d, AnyPin>, config: Config, ) -> Self { - into_ref!(irq); + into_ref!(pdm); - let r = Self::regs(); + let r = T::regs(); // setup gpio pins din.conf().write(|w| w.input().set_bit()); @@ -84,26 +94,18 @@ impl<'d> Pdm<'d> { r.gainr.write(|w| w.gainr().default_gain()); // IRQ - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - }); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); r.enable.write(|w| w.enable().set_bit()); - Self { - phantom: PhantomData, - irq, - } + Self { _peri: pdm } } /// Start sampling microphon data into a dummy buffer /// Usefull to start the microphon and keep it active between recording samples pub async fn start(&mut self) { - let r = Self::regs(); + let r = T::regs(); // start dummy sampling because microphon needs some setup time r.sample @@ -113,13 +115,13 @@ impl<'d> Pdm<'d> { .maxcnt .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - r.tasks_start.write(|w| w.tasks_start().set_bit()); + r.tasks_start.write(|w| unsafe { w.bits(1) }); } /// Stop sampling microphon data inta a dummy buffer pub async fn stop(&mut self) { - let r = Self::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.events_started.reset(); } @@ -132,9 +134,9 @@ impl<'d> Pdm<'d> { return Err(Error::BufferTooLong); } - let r = Self::regs(); + let r = T::regs(); - if r.events_started.read().events_started().bit_is_clear() { + if r.events_started.read().bits() == 0 { return Err(Error::NotRunning); } @@ -179,7 +181,7 @@ impl<'d> Pdm<'d> { } async fn wait_for_sample() { - let r = Self::regs(); + let r = T::regs(); r.events_end.reset(); r.intenset.write(|w| w.end().set()); @@ -187,8 +189,8 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); poll_fn(|cx| { - WAKER.register(cx.waker()); - if r.events_end.read().events_end().bit_is_set() { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { return Poll::Ready(()); } Poll::Pending @@ -197,10 +199,6 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); } - - fn regs() -> &'static pac::pdm::RegisterBlock { - unsafe { &*pac::PDM::ptr() } - } } /// PDM microphone driver Config @@ -238,13 +236,11 @@ pub enum Edge { LeftFalling, } -impl<'d> Drop for Pdm<'d> { +impl<'d, T: Instance> Drop for Pdm<'d, T> { fn drop(&mut self) { - let r = Self::regs(); + let r = T::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); - - self.irq.disable(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.enable.write(|w| w.enable().disabled()); @@ -252,3 +248,48 @@ impl<'d> Drop for Pdm<'d> { r.psel.clk.reset(); } } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; + } +} + +/// PDM peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_pdm { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::pdm::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::pdm::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::pdm::sealed::State { + static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + &STATE + } + } + impl crate::pdm::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 7388580f..6b41320c 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -4,16 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::pdm::{Config, Pdm}; +use embassy_nrf::pdm::{self, Config, Pdm}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let p = embassy_nrf::init(Default::default()); let config = Config::default(); - let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { pdm.start().await; From c66b28e759dc42c5f802336385a66eb8a82dab9a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:28:13 +0100 Subject: [PATCH 07/17] nrf/qdec: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 2 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_app.rs | 9 ++- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/qdec.rs | 112 +++++++++++++++++++-------- examples/nrf52840/src/bin/qdec.rs | 9 ++- 11 files changed, 108 insertions(+), 38 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 3c74a2a6..185cda43 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -140,6 +140,8 @@ impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index c6ccb6a0..1e3c054a 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -150,6 +150,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 7d1bce1c..3bb44171 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -152,6 +152,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 81b07f32..b28778f3 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -153,6 +153,8 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index ce19a18e..00dc9fd8 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -173,6 +173,8 @@ impl_pwm!(PWM2, PWM2, PWM2); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 08b82021..345608c9 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -199,6 +199,8 @@ impl_pwm!(PWM3, PWM3, PWM3); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4e8b6d9e..630f01aa 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -210,6 +210,8 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 050612b1..34f96800 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -37,7 +37,7 @@ pub mod pac { pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, - qdec0_ns as qdec0, + qdec0_ns as qdec, qspi_ns as qspi, regulators_ns as regulators, reset_ns as reset, @@ -256,6 +256,10 @@ embassy_hal_common::peripherals! { // PDM PDM0, + // QDEC + QDEC0, + QDEC1, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -403,6 +407,9 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM0, PDM0, PDM0); +impl_qdec!(QDEC0, QDEC0, QDEC0); +impl_qdec!(QDEC1, QDEC1, QDEC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 17aa5ad1..feefa248 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -57,7 +57,7 @@ pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; -#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] +#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))] pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index c01babca..4d2a0919 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,20 +1,22 @@ //! Quadrature decoder (QDEC) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::InterruptExt; -use crate::peripherals::QDEC; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; /// Quadrature decoder driver. -pub struct Qdec<'d> { - _p: PeripheralRef<'d, QDEC>, +pub struct Qdec<'d, T: Instance> { + _p: PeripheralRef<'d, T>, } /// QDEC config @@ -44,44 +46,52 @@ impl Default for Config { } } -static WAKER: AtomicWaker = AtomicWaker::new(); +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} -impl<'d> Qdec<'d> { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.reportrdy().clear()); + T::state().waker.wake(); + } +} + +impl<'d, T: Instance> Qdec<'d, T> { /// Create a new QDEC. pub fn new( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) + into_ref!(qdec, a, b); + Self::new_inner(qdec, a.map_into(), b.map_into(), None, config) } /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, led: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b, led); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config) + into_ref!(qdec, a, b, led); + Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config) } fn new_inner( - p: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + p: PeripheralRef<'d, T>, a: PeripheralRef<'d, AnyPin>, b: PeripheralRef<'d, AnyPin>, led: Option>, config: Config, ) -> Self { - into_ref!(p, irq); - let r = Self::regs(); + let r = T::regs(); // Select pins. a.conf().write(|w| w.input().connect().pull().pullup()); @@ -124,20 +134,15 @@ impl<'d> Qdec<'d> { SamplePeriod::_131ms => w.sampleper()._131ms(), }); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + // Enable peripheral r.enable.write(|w| w.enable().set_bit()); // Start sampling unsafe { r.tasks_start.write(|w| w.bits(1)) }; - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.reportrdy().clear()); - WAKER.wake(); - }); - irq.enable(); - Self { _p: p } } @@ -155,12 +160,12 @@ impl<'d> Qdec<'d> { /// let delta = q.read().await; /// ``` pub async fn read(&mut self) -> i16 { - let t = Self::regs(); + let t = T::regs(); t.intenset.write(|w| w.reportrdy().set()); unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; let value = poll_fn(|cx| { - WAKER.register(cx.waker()); + T::state().waker.register(cx.waker()); if t.events_reportrdy.read().bits() == 0 { return Poll::Pending; } else { @@ -172,10 +177,6 @@ impl<'d> Qdec<'d> { .await; value } - - fn regs() -> &'static pac::qdec::RegisterBlock { - unsafe { &*pac::QDEC::ptr() } - } } /// Sample period @@ -236,3 +237,48 @@ pub enum LedPolarity { /// Active low (a low output turns on the LED). ActiveLow, } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::qdec::RegisterBlock; + fn state() -> &'static State; + } +} + +/// qdec peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_qdec { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::qdec::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::qdec::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::qdec::sealed::State { + static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new(); + &STATE + } + } + impl crate::qdec::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs index 600bba07..59783d31 100644 --- a/examples/nrf52840/src/bin/qdec.rs +++ b/examples/nrf52840/src/bin/qdec.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::qdec::{self, Qdec}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + QDEC => qdec::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(QDEC); let config = qdec::Config::default(); - let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); + let mut rotary_enc = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config); info!("Turn rotary encoder!"); let mut value = 0; From 96788ac93a1e98ef8d9d5e8d80d5102aef34d45d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:37:21 +0100 Subject: [PATCH 08/17] nrf/qspi: switch to new interrupt binding. --- embassy-nrf/src/qspi.rs | 65 ++++++++++++---------- examples/nrf52840/src/bin/qspi.rs | 9 ++- examples/nrf52840/src/bin/qspi_lowpower.rs | 9 ++- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d514e027..7f004b9f 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::task::Poll; @@ -11,12 +12,12 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; pub use crate::pac::qspi::ifconfig0::{ ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, }; pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; -use crate::{pac, Peripheral}; +use crate::Peripheral; /// Deep power-down config. pub struct DeepPowerDownConfig { @@ -114,9 +115,26 @@ pub enum Error { // TODO add "not in data memory" error and check for it } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_ready.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.ready().clear()); + } + } +} + /// QSPI flash driver. pub struct Qspi<'d, T: Instance> { - irq: PeripheralRef<'d, T::Interrupt>, + _peri: PeripheralRef<'d, T>, dpm_enabled: bool, capacity: u32, } @@ -124,8 +142,8 @@ pub struct Qspi<'d, T: Instance> { impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( - _qspi: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qspi: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, csn: impl Peripheral

+ 'd, io0: impl Peripheral

+ 'd, @@ -134,7 +152,7 @@ impl<'d, T: Instance> Qspi<'d, T> { io3: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(irq, sck, csn, io0, io1, io2, io3); + into_ref!(qspi, sck, csn, io0, io1, io2, io3); let r = T::regs(); @@ -189,16 +207,15 @@ impl<'d, T: Instance> Qspi<'d, T> { w }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); // Enable it r.enable.write(|w| w.enable().enabled()); let res = Self { + _peri: qspi, dpm_enabled: config.deep_power_down.is_some(), - irq, capacity: config.capacity, }; @@ -212,16 +229,6 @@ impl<'d, T: Instance> Qspi<'d, T> { res } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_ready.read().bits() != 0 { - s.ready_waker.wake(); - r.intenclr.write(|w| w.ready().clear()); - } - } - /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); @@ -310,7 +317,7 @@ impl<'d, T: Instance> Qspi<'d, T> { poll_fn(move |cx| { let r = T::regs(); let s = T::state(); - s.ready_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.events_ready.read().bits() != 0 { return Poll::Ready(()); } @@ -525,8 +532,6 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { r.enable.write(|w| w.enable().disabled()); - self.irq.disable(); - // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, // leaving it floating, the flash chip might read it as zero which would cause it to // spuriously exit DPM. @@ -624,27 +629,27 @@ mod _eh1 { pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; - use super::*; - + /// Peripheral static state pub struct State { - pub ready_waker: AtomicWaker, + pub waker: AtomicWaker, } + impl State { pub const fn new() -> Self { Self { - ready_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), } } } pub trait Instance { - fn regs() -> &'static pac::qspi::RegisterBlock; + fn regs() -> &'static crate::pac::qspi::RegisterBlock; fn state() -> &'static State; } } /// QSPI peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -652,7 +657,7 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static { macro_rules! impl_qspi { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::qspi::sealed::Instance for peripherals::$type { - fn regs() -> &'static pac::qspi::RegisterBlock { + fn regs() -> &'static crate::pac::qspi::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } fn state() -> &'static crate::qspi::sealed::State { diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index 21a10940..9e8a01f4 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -5,7 +5,7 @@ use defmt::{assert_eq, info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::qspi::Frequency; -use embassy_nrf::{interrupt, qspi}; +use embassy_nrf::{bind_interrupts, peripherals, qspi}; use {defmt_rtt as _, panic_probe as _}; const PAGE_SIZE: usize = 4096; @@ -15,6 +15,10 @@ const PAGE_SIZE: usize = 4096; #[repr(C, align(4))] struct AlignedBuf([u8; 4096]); +bind_interrupts!(struct Irqs { + QSPI => qspi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +30,8 @@ async fn main(_spawner: Spawner) { config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; - let irq = interrupt::take!(QSPI); let mut q = qspi::Qspi::new( - p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, + p.QSPI, Irqs, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, ); let mut id = [1; 3]; diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 20c90391..22a5c0c6 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::qspi::Frequency; -use embassy_nrf::{interrupt, qspi}; +use embassy_nrf::{bind_interrupts, peripherals, qspi}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,10 +16,13 @@ use {defmt_rtt as _, panic_probe as _}; #[repr(C, align(4))] struct AlignedBuf([u8; 64]); +bind_interrupts!(struct Irqs { + QSPI => qspi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); - let mut irq = interrupt::take!(QSPI); loop { // Config for the MX25R64 present in the nRF52840 DK @@ -36,7 +39,7 @@ async fn main(_p: Spawner) { let mut q = qspi::Qspi::new( &mut p.QSPI, - &mut irq, + Irqs, &mut p.P0_19, &mut p.P0_17, &mut p.P0_20, From d113fcfe326bd338df2db7733fcf0ae9f230c594 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:50:15 +0100 Subject: [PATCH 09/17] nrf/rng: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 2 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_net.rs | 5 + embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/rng.rs | 193 ++++++++++++++-------- examples/nrf52840/src/bin/rng.rs | 8 +- examples/nrf52840/src/bin/usb_ethernet.rs | 8 +- 12 files changed, 154 insertions(+), 76 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 185cda43..e406c081 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -142,6 +142,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 1e3c054a..153795e5 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -152,6 +152,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 3bb44171..a7a7cf58 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -154,6 +154,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index b28778f3..14a1b8cc 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -155,6 +155,8 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 00dc9fd8..83ecd0de 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -175,6 +175,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 345608c9..5e5db04d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -201,6 +201,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 630f01aa..f6d33f85 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -212,6 +212,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d7ba6c16..1e59528c 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -127,6 +127,9 @@ embassy_hal_common::peripherals! { // SAADC SAADC, + // RNG + RNG, + // PWM PWM0, PWM1, @@ -252,6 +255,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index feefa248..ee29ce20 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -61,7 +61,7 @@ pub mod pwm; pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; -#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] +#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))] pub mod rng; #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index b0b3a8eb..a5602248 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,83 +1,48 @@ //! Random Number Generator (RNG) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; -use crate::peripherals::RNG; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; -impl RNG { - fn regs() -> &'static pac::rng::RegisterBlock { - unsafe { &*pac::RNG::ptr() } - } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -static STATE: State = State { - ptr: AtomicPtr::new(ptr::null_mut()), - end: AtomicPtr::new(ptr::null_mut()), - waker: AtomicWaker::new(), -}; +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let s = T::state(); + let r = T::regs(); -struct State { - ptr: AtomicPtr, - end: AtomicPtr, - waker: AtomicWaker, -} - -/// A wrapper around an nRF RNG peripheral. -/// -/// It has a non-blocking API, and a blocking api through `rand`. -pub struct Rng<'d> { - irq: PeripheralRef<'d, interrupt::RNG>, -} - -impl<'d> Rng<'d> { - /// Creates a new RNG driver from the `RNG` peripheral and interrupt. - /// - /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, - /// e.g. using `mem::forget`. - /// - /// The synchronous API is safe. - pub fn new(_rng: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - let this = Self { irq }; - - this.stop(); - this.disable_irq(); - - this.irq.set_handler(Self::on_interrupt); - this.irq.unpend(); - this.irq.enable(); - - this - } - - fn on_interrupt(_: *mut ()) { // Clear the event. - RNG::regs().events_valrdy.reset(); + r.events_valrdy.reset(); // Mutate the slice within a critical section, // so that the future isn't dropped in between us loading the pointer and actually dereferencing it. let (ptr, end) = critical_section::with(|_| { - let ptr = STATE.ptr.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); // We need to make sure we haven't already filled the whole slice, // in case the interrupt fired again before the executor got back to the future. - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); if !ptr.is_null() && ptr != end { // If the future was dropped, the pointer would have been set to null, // so we're still good to mutate the slice. // The safety contract of `Rng::new` means that the future can't have been dropped // without calling its destructor. unsafe { - *ptr = RNG::regs().value.read().value().bits(); + *ptr = r.value.read().value().bits(); } } (ptr, end) @@ -90,15 +55,15 @@ impl<'d> Rng<'d> { } let new_ptr = unsafe { ptr.add(1) }; - match STATE + match s .ptr .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => { - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); // It doesn't matter if `end` was changed under our feet, because then this will just be false. if new_ptr == end { - STATE.waker.wake(); + s.waker.wake(); } } Err(_) => { @@ -107,21 +72,53 @@ impl<'d> Rng<'d> { } } } +} + +/// A wrapper around an nRF RNG peripheral. +/// +/// It has a non-blocking API, and a blocking api through `rand`. +pub struct Rng<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Rng<'d, T> { + /// Creates a new RNG driver from the `RNG` peripheral and interrupt. + /// + /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, + /// e.g. using `mem::forget`. + /// + /// The synchronous API is safe. + pub fn new( + rng: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + ) -> Self { + into_ref!(rng); + + let this = Self { _peri: rng }; + + this.stop(); + this.disable_irq(); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + this + } fn stop(&self) { - RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) } fn start(&self) { - RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_start.write(|w| unsafe { w.bits(1) }) } fn enable_irq(&self) { - RNG::regs().intenset.write(|w| w.valrdy().set()); + T::regs().intenset.write(|w| w.valrdy().set()); } fn disable_irq(&self) { - RNG::regs().intenclr.write(|w| w.valrdy().clear()); + T::regs().intenclr.write(|w| w.valrdy().clear()); } /// Enable or disable the RNG's bias correction. @@ -131,7 +128,7 @@ impl<'d> Rng<'d> { /// /// Defaults to disabled. pub fn set_bias_correction(&self, enable: bool) { - RNG::regs().config.write(|w| w.dercen().bit(enable)) + T::regs().config.write(|w| w.dercen().bit(enable)) } /// Fill the buffer with random bytes. @@ -140,11 +137,13 @@ impl<'d> Rng<'d> { return; // Nothing to fill } + let s = T::state(); + let range = dest.as_mut_ptr_range(); // Even if we've preempted the interrupt, it can't preempt us again, // so we don't need to worry about the order we write these in. - STATE.ptr.store(range.start, Ordering::Relaxed); - STATE.end.store(range.end, Ordering::Relaxed); + s.ptr.store(range.start, Ordering::Relaxed); + s.end.store(range.end, Ordering::Relaxed); self.enable_irq(); self.start(); @@ -154,16 +153,16 @@ impl<'d> Rng<'d> { self.disable_irq(); // The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here. - STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed); - STATE.end.store(ptr::null_mut(), Ordering::Relaxed); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); }); poll_fn(|cx| { - STATE.waker.register(cx.waker()); + s.waker.register(cx.waker()); // The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`. - let end = STATE.end.load(Ordering::Relaxed); - let ptr = STATE.ptr.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); if ptr == end { // We're done. @@ -183,7 +182,7 @@ impl<'d> Rng<'d> { self.start(); for byte in dest.iter_mut() { - let regs = RNG::regs(); + let regs = T::regs(); while regs.events_valrdy.read().bits() == 0 {} regs.events_valrdy.reset(); *byte = regs.value.read().value().bits(); @@ -193,13 +192,16 @@ impl<'d> Rng<'d> { } } -impl<'d> Drop for Rng<'d> { +impl<'d, T: Instance> Drop for Rng<'d, T> { fn drop(&mut self) { - self.irq.disable() + self.stop(); + let s = T::state(); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); } } -impl<'d> rand_core::RngCore for Rng<'d> { +impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> { fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest); } @@ -223,4 +225,53 @@ impl<'d> rand_core::RngCore for Rng<'d> { } } -impl<'d> rand_core::CryptoRng for Rng<'d> {} +impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {} + +pub(crate) mod sealed { + use super::*; + + /// Peripheral static state + pub struct State { + pub ptr: AtomicPtr, + pub end: AtomicPtr, + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + ptr: AtomicPtr::new(ptr::null_mut()), + end: AtomicPtr::new(ptr::null_mut()), + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::rng::RegisterBlock; + fn state() -> &'static State; + } +} + +/// RNG peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_rng { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::rng::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::rng::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::rng::sealed::State { + static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new(); + &STATE + } + } + impl crate::rng::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs index 64707394..855743f5 100644 --- a/examples/nrf52840/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs @@ -3,15 +3,19 @@ #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, peripherals, rng}; use rand::Rng as _; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); // Async API let mut bytes = [0; 4]; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 97978089..083a1cbb 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -10,7 +10,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -18,6 +18,10 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; macro_rules! singleton { @@ -108,7 +112,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.blocking_fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); From 2dc56082033f650083355464c3106ccb57302338 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:56:22 +0100 Subject: [PATCH 10/17] nrf/saadc: switch to new interrupt binding. --- embassy-nrf/src/saadc.rs | 62 ++++++++++--------- examples/nrf52840/src/bin/saadc.rs | 8 ++- examples/nrf52840/src/bin/saadc_continuous.rs | 8 ++- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 2d01a3dd..af952f03 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,6 +6,7 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -17,7 +18,6 @@ use saadc::oversample::OVERSAMPLE_A; use saadc::resolution::VAL_A; use self::sealed::Input as _; -use crate::interrupt::InterruptExt; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; @@ -28,9 +28,30 @@ use crate::{interrupt, pac, peripherals, Peripheral}; #[non_exhaustive] pub enum Error {} -/// One-shot and continuous SAADC. -pub struct Saadc<'d, const N: usize> { - _p: PeripheralRef<'d, peripherals::SAADC>, +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = unsafe { &*SAADC::ptr() }; + + if r.events_calibratedone.read().bits() != 0 { + r.intenclr.write(|w| w.calibratedone().clear()); + WAKER.wake(); + } + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + WAKER.wake(); + } + } } static WAKER: AtomicWaker = AtomicWaker::new(); @@ -114,15 +135,20 @@ pub enum CallbackResult { Stop, } +/// One-shot and continuous SAADC. +pub struct Saadc<'d, const N: usize> { + _p: PeripheralRef<'d, peripherals::SAADC>, +} + impl<'d, const N: usize> Saadc<'d, N> { /// Create a new SAADC driver. pub fn new( saadc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, config: Config, channel_configs: [ChannelConfig; N], ) -> Self { - into_ref!(saadc, irq); + into_ref!(saadc); let r = unsafe { &*SAADC::ptr() }; @@ -163,32 +189,12 @@ impl<'d, const N: usize> Saadc<'d, N> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { interrupt::SAADC::steal() }.unpend(); + unsafe { interrupt::SAADC::steal() }.enable(); Self { _p: saadc } } - fn on_interrupt(_ctx: *mut ()) { - let r = Self::regs(); - - if r.events_calibratedone.read().bits() != 0 { - r.intenclr.write(|w| w.calibratedone().clear()); - WAKER.wake(); - } - - if r.events_end.read().bits() != 0 { - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - } - - if r.events_started.read().bits() != 0 { - r.intenclr.write(|w| w.started().clear()); - WAKER.wake(); - } - } - fn regs() -> &'static saadc::RegisterBlock { unsafe { &*SAADC::ptr() } } diff --git a/examples/nrf52840/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs index 7cf58809..ffd9a7f4 100644 --- a/examples/nrf52840/src/bin/saadc.rs +++ b/examples/nrf52840/src/bin/saadc.rs @@ -4,17 +4,21 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; +use embassy_nrf::{bind_interrupts, saadc}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); let config = Config::default(); let channel_config = ChannelConfig::single_ended(&mut p.P0_02); - let mut saadc = Saadc::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]); + let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]); loop { let mut buf = [0; 1]; diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index 2551d15f..a25e1746 100644 --- a/examples/nrf52840/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs @@ -4,14 +4,18 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; use embassy_nrf::timer::Frequency; +use embassy_nrf::{bind_interrupts, saadc}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); @@ -21,7 +25,7 @@ async fn main(_p: Spawner) { let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); let mut saadc = Saadc::new( p.SAADC, - interrupt::take!(SAADC), + Irqs, config, [channel_1_config, channel_2_config, channel_3_config], ); From a32e82029a8b6944ef3e9861b09095bae01b37a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:00:52 +0100 Subject: [PATCH 11/17] nrf/spim: switch to new interrupt binding. --- embassy-nrf/src/spim.rs | 59 +++++++++++--------- examples/nrf52840/src/bin/lora_p2p_report.rs | 9 ++- examples/nrf52840/src/bin/lora_p2p_sense.rs | 9 ++- examples/nrf52840/src/bin/spim.rs | 9 ++- 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 17e43578..89cbdfee 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -14,7 +15,7 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -31,11 +32,6 @@ pub enum Error { BufferNotInRAM, } -/// SPIM driver. -pub struct Spim<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - /// SPIM configuration. #[non_exhaustive] pub struct Config { @@ -62,11 +58,33 @@ impl Default for Config { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + } +} + +/// SPIM driver. +pub struct Spim<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver. pub fn new( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, @@ -75,7 +93,6 @@ impl<'d, T: Instance> Spim<'d, T> { into_ref!(sck, miso, mosi); Self::new_inner( spim, - irq, sck.map_into(), Some(miso.map_into()), Some(mosi.map_into()), @@ -86,36 +103,35 @@ impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver, capable of TX only (MOSI only). pub fn new_txonly( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(sck, mosi); - Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) + Self::new_inner(spim, sck.map_into(), None, Some(mosi.map_into()), config) } /// Create a new SPIM driver, capable of RX only (MISO only). pub fn new_rxonly( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(sck, miso); - Self::new_inner(spim, irq, sck.map_into(), Some(miso.map_into()), None, config) + Self::new_inner(spim, sck.map_into(), Some(miso.map_into()), None, config) } fn new_inner( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, sck: PeripheralRef<'d, AnyPin>, miso: Option>, mosi: Option>, config: Config, ) -> Self { - into_ref!(spim, irq); + into_ref!(spim); let r = T::regs(); @@ -191,23 +207,12 @@ impl<'d, T: Instance> Spim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: spim } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_end.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.end().clear()); - } - } - fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs index d512b83f..e24f0db0 100644 --- a/examples/nrf52840/src/bin/lora_p2p_report.rs +++ b/examples/nrf52840/src/bin/lora_p2p_report.rs @@ -11,11 +11,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -23,8 +27,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = spim::Frequency::M16; let mut radio = { - let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs index b9768874..b6f41ffc 100644 --- a/examples/nrf52840/src/bin/lora_p2p_sense.rs +++ b/examples/nrf52840/src/bin/lora_p2p_sense.rs @@ -12,13 +12,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher}; use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; use {defmt_rtt as _, panic_probe as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); @@ -58,8 +62,7 @@ async fn main(spawner: Spawner) { spi_config.frequency = spim::Frequency::M16; let mut radio = { - let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); diff --git a/examples/nrf52840/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs index 132e0166..9d1843a8 100644 --- a/examples/nrf52840/src/bin/spim.rs +++ b/examples/nrf52840/src/bin/spim.rs @@ -5,9 +5,13 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::gpio::{Level, Output, OutputDrive}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -16,8 +20,7 @@ async fn main(_spawner: Spawner) { let mut config = spim::Config::default(); config.frequency = spim::Frequency::M16; - let irq = interrupt::take!(SPIM3); - let mut spim = spim::Spim::new(p.SPI3, irq, p.P0_29, p.P0_28, p.P0_30, config); + let mut spim = spim::Spim::new(p.SPI3, Irqs, p.P0_29, p.P0_28, p.P0_30, config); let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard); From 9f5762d3654464011b5ddda771d4866c547106a0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:05:02 +0100 Subject: [PATCH 12/17] nrf/spis: switch to new interrupt binding. --- embassy-nrf/src/spis.rs | 85 ++++++++++++++----------------- examples/nrf52840/src/bin/spis.rs | 9 ++-- 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 1b743647..55b5e060 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -2,6 +2,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -12,7 +13,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -29,11 +30,6 @@ pub enum Error { BufferNotInRAM, } -/// SPIS driver. -pub struct Spis<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - /// SPIS configuration. #[non_exhaustive] pub struct Config { @@ -67,11 +63,38 @@ impl Default for Config { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_acquired.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.acquired().clear()); + } + } +} + +/// SPIS driver. +pub struct Spis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver. pub fn new( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, @@ -81,7 +104,6 @@ impl<'d, T: Instance> Spis<'d, T> { into_ref!(cs, sck, miso, mosi); Self::new_inner( spis, - irq, cs.map_into(), sck.map_into(), Some(miso.map_into()), @@ -93,48 +115,31 @@ impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver, capable of TX only (MISO only). pub fn new_txonly( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, miso); - Self::new_inner( - spis, - irq, - cs.map_into(), - sck.map_into(), - Some(miso.map_into()), - None, - config, - ) + Self::new_inner(spis, cs.map_into(), sck.map_into(), Some(miso.map_into()), None, config) } /// Create a new SPIS driver, capable of RX only (MOSI only). pub fn new_rxonly( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, mosi); - Self::new_inner( - spis, - irq, - cs.map_into(), - sck.map_into(), - None, - Some(mosi.map_into()), - config, - ) + Self::new_inner(spis, cs.map_into(), sck.map_into(), None, Some(mosi.map_into()), config) } fn new_inner( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, cs: PeripheralRef<'d, AnyPin>, sck: PeripheralRef<'d, AnyPin>, miso: Option>, @@ -143,7 +148,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) -> Self { compiler_fence(Ordering::SeqCst); - into_ref!(spis, irq, cs, sck); + into_ref!(spis, cs, sck); let r = T::regs(); @@ -209,28 +214,12 @@ impl<'d, T: Instance> Spis<'d, T> { // Disable all events interrupts. r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: spis } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_end.read().bits() != 0 { - s.waker.wake(); - r.intenclr.write(|w| w.end().clear()); - } - - if r.events_acquired.read().bits() != 0 { - s.waker.wake(); - r.intenclr.write(|w| w.acquired().clear()); - } - } - fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs index fe3b0c53..77b6e8b6 100644 --- a/examples/nrf52840/src/bin/spis.rs +++ b/examples/nrf52840/src/bin/spis.rs @@ -4,17 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::spis::{Config, Spis}; +use embassy_nrf::{bind_interrupts, peripherals, spis}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM2_SPIS2_SPI2 => spis::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Running!"); - let irq = interrupt::take!(SPIM2_SPIS2_SPI2); - let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + let mut spis = Spis::new(p.SPI2, Irqs, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); loop { let mut rx_buf = [0_u8; 64]; From 9e58d9274c63ebe48217e94162e47bea3211ff1c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:12:34 +0100 Subject: [PATCH 13/17] nrf/twim: switch to new interrupt binding. --- embassy-nrf/src/twim.rs | 47 +++++++++++++--------- examples/nrf52840/src/bin/twim.rs | 9 +++-- examples/nrf52840/src/bin/twim_lowpower.rs | 9 +++-- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 0dcb2b0d..ef4c929a 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::{poll_fn, Future}; +use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -15,7 +16,7 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Peripheral}; @@ -92,6 +93,27 @@ pub enum Error { Timeout, } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_stopped.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.error().clear()); + } + } +} + /// TWI driver. pub struct Twim<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -101,12 +123,12 @@ impl<'d, T: Instance> Twim<'d, T> { /// Create a new TWI driver. pub fn new( twim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(twim, irq, sda, scl); + into_ref!(twim, sda, scl); let r = T::regs(); @@ -152,27 +174,12 @@ impl<'d, T: Instance> Twim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: twim } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_stopped.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.stopped().clear()); - } - if r.events_error.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.error().clear()); - } - } - /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { slice_in_ram_or(buffer, Error::BufferNotInRAM)?; diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index a027cc1e..959e3a4b 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs @@ -8,19 +8,22 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twim::{self, Twim}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Initializing TWI..."); let config = twim::Config::default(); - let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); info!("Reading..."); diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index e30cc968..0970d3c3 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs @@ -12,25 +12,28 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twim::{self, Twim}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); info!("Started!"); - let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); loop { info!("Initializing TWI..."); let config = twim::Config::default(); // Create the TWIM instance with borrowed singletons, so they're not consumed. - let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); + let mut twi = Twim::new(&mut p.TWISPI0, Irqs, &mut p.P0_03, &mut p.P0_04, config); info!("Reading..."); From 36319fc121f19f86dded45b6fb93aed7c3f4ae33 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:09:54 +0100 Subject: [PATCH 14/17] nrf/temp: switch to new interrupt binding. --- embassy-nrf/src/temp.rs | 35 +++++++++++++++++++++---------- examples/nrf52840/src/bin/temp.rs | 9 +++++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 5298faab..3a75ec62 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,6 +3,7 @@ use core::future::poll_fn; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -12,27 +13,39 @@ use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = unsafe { &*pac::TEMP::PTR }; + r.intenclr.write(|w| w.datardy().clear()); + WAKER.wake(); + } +} + /// Builtin temperature sensor driver. pub struct Temp<'d> { - _irq: PeripheralRef<'d, interrupt::TEMP>, + _peri: PeripheralRef<'d, TEMP>, } static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Temp<'d> { /// Create a new temperature sensor driver. - pub fn new(_t: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(_t, irq); + pub fn new( + _peri: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, + ) -> Self { + into_ref!(_peri); // Enable interrupt that signals temperature values - irq.disable(); - irq.set_handler(|_| { - let t = Self::regs(); - t.intenclr.write(|w| w.datardy().clear()); - WAKER.wake(); - }); - irq.enable(); - Self { _irq: irq } + unsafe { interrupt::TEMP::steal() }.unpend(); + unsafe { interrupt::TEMP::steal() }.enable(); + + Self { _peri } } /// Perform an asynchronous temperature measurement. The returned future diff --git a/examples/nrf52840/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs index b06ac709..70957548 100644 --- a/examples/nrf52840/src/bin/temp.rs +++ b/examples/nrf52840/src/bin/temp.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::temp::Temp; +use embassy_nrf::{bind_interrupts, temp}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + TEMP => temp::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(TEMP); - let mut temp = Temp::new(p.TEMP, irq); + let mut temp = Temp::new(p.TEMP, Irqs); loop { let value = temp.read().await; From 5913553cb1e95431665d3370dce8154a6869e434 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:14:59 +0100 Subject: [PATCH 15/17] nrf/twis: switch to new interrupt binding. --- embassy-nrf/src/twis.rs | 55 +++++++++++++++++-------------- examples/nrf52840/src/bin/twis.rs | 12 ++++--- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index c514d9f2..bfa30b04 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::{poll_fn, Future}; +use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -14,7 +15,7 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::slice_in_ram_or; use crate::{gpio, pac, Peripheral}; @@ -108,6 +109,31 @@ pub enum Command { Write(usize), } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.read().clear().write().clear()); + } + if r.events_stopped.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.error().clear()); + } + } +} + /// TWIS driver. pub struct Twis<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -117,12 +143,12 @@ impl<'d, T: Instance> Twis<'d, T> { /// Create a new TWIS driver. pub fn new( twis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(twis, irq, sda, scl); + into_ref!(twis, sda, scl); let r = T::regs(); @@ -178,31 +204,12 @@ impl<'d, T: Instance> Twis<'d, T> { // Generate suspend on read event r.shorts.write(|w| w.read_suspend().enabled()); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: twis } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { - s.waker.wake(); - r.intenclr.modify(|_r, w| w.read().clear().write().clear()); - } - if r.events_stopped.read().bits() != 0 { - s.waker.wake(); - r.intenclr.modify(|_r, w| w.stopped().clear()); - } - if r.events_error.read().bits() != 0 { - s.waker.wake(); - r.intenclr.modify(|_r, w| w.error().clear()); - } - } - /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { slice_in_ram_or(buffer, Error::BufferNotInRAM)?; diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs index 54cba949..aa42b679 100644 --- a/examples/nrf52840/src/bin/twis.rs +++ b/examples/nrf52840/src/bin/twis.rs @@ -6,19 +6,21 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twis::{self, Command, Twis}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); let mut config = twis::Config::default(); - // Set i2c address - config.address0 = 0x55; - let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + config.address0 = 0x55; // Set i2c address + let mut i2c = Twis::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); info!("Listening..."); loop { From 5249996d282e1ae08cea1593193e2fe6ca20880a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:36:53 +0100 Subject: [PATCH 16/17] nrf/usb: switch to new interrupt binding, fix vbus detect on nrf53. --- embassy-nrf/src/{usb.rs => usb/mod.rs} | 234 ++++-------------- embassy-nrf/src/usb/vbus_detect.rs | 177 +++++++++++++ examples/nrf52840/Cargo.toml | 7 +- examples/nrf52840/src/bin/usb_ethernet.rs | 12 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 16 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 16 +- examples/nrf52840/src/bin/usb_serial.rs | 16 +- .../nrf52840/src/bin/usb_serial_multitask.rs | 51 ++-- .../nrf52840/src/bin/usb_serial_winusb.rs | 14 +- 9 files changed, 300 insertions(+), 243 deletions(-) rename embassy-nrf/src/{usb.rs => usb/mod.rs} (81%) create mode 100644 embassy-nrf/src/usb/vbus_detect.rs diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb/mod.rs similarity index 81% rename from embassy-nrf/src/usb.rs rename to embassy-nrf/src/usb/mod.rs index cd142f00..56de511d 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -2,10 +2,12 @@ #![macro_use] +pub mod vbus_detect; + use core::future::poll_fn; use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; @@ -15,7 +17,8 @@ use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; use pac::usbd::RegisterBlock; -use crate::interrupt::{Interrupt, InterruptExt}; +use self::vbus_detect::VbusDetect; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::slice_in_ram; use crate::{pac, Peripheral}; @@ -26,185 +29,13 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); -/// Trait for detecting USB VBUS power. -/// -/// There are multiple ways to detect USB power. The behavior -/// here provides a hook into determining whether it is. -pub trait VbusDetect { - /// Report whether power is detected. - /// - /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the - /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - fn is_usb_detected(&self) -> bool; - - /// Wait until USB power is ready. - /// - /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the - /// `USBPWRRDY` event from the `POWER` peripheral. - async fn wait_power_ready(&mut self) -> Result<(), ()>; +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -/// [`VbusDetect`] implementation using the native hardware POWER peripheral. -/// -/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces -/// to POWER. In that case, use [`VbusDetectSignal`]. -#[cfg(not(feature = "_nrf5340-app"))] -pub struct HardwareVbusDetect { - _private: (), -} - -static POWER_WAKER: AtomicWaker = NEW_AW; - -#[cfg(not(feature = "_nrf5340-app"))] -impl HardwareVbusDetect { - /// Create a new `VbusDetectNative`. - pub fn new(power_irq: impl Interrupt) -> Self { - let regs = unsafe { &*pac::POWER::ptr() }; - - power_irq.set_handler(Self::on_interrupt); - power_irq.unpend(); - power_irq.enable(); - - regs.intenset - .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); - - Self { _private: () } - } - - #[cfg(not(feature = "_nrf5340-app"))] - fn on_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - BUS_WAKER.wake(); - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - if regs.events_usbpwrrdy.read().bits() != 0 { - regs.events_usbpwrrdy.reset(); - POWER_WAKER.wake(); - } - } -} - -#[cfg(not(feature = "_nrf5340-app"))] -impl VbusDetect for HardwareVbusDetect { - fn is_usb_detected(&self) -> bool { - let regs = unsafe { &*pac::POWER::ptr() }; - regs.usbregstatus.read().vbusdetect().is_vbus_present() - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.usbregstatus.read().outputrdy().is_ready() { - Poll::Ready(Ok(())) - } else if !self.is_usb_detected() { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// Software-backed [`VbusDetect`] implementation. -/// -/// This implementation does not interact with the hardware, it allows user code -/// to notify the power events by calling functions instead. -/// -/// This is suitable for use with the nRF softdevice, by calling the functions -/// when the softdevice reports power-related events. -pub struct SoftwareVbusDetect { - usb_detected: AtomicBool, - power_ready: AtomicBool, -} - -impl SoftwareVbusDetect { - /// Create a new `SoftwareVbusDetect`. - pub fn new(usb_detected: bool, power_ready: bool) -> Self { - BUS_WAKER.wake(); - - Self { - usb_detected: AtomicBool::new(usb_detected), - power_ready: AtomicBool::new(power_ready), - } - } - - /// Report whether power was detected. - /// - /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - pub fn detected(&self, detected: bool) { - self.usb_detected.store(detected, Ordering::Relaxed); - self.power_ready.store(false, Ordering::Relaxed); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - /// Report when USB power is ready. - /// - /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. - pub fn ready(&self) { - self.power_ready.store(true, Ordering::Relaxed); - POWER_WAKER.wake(); - } -} - -impl VbusDetect for &SoftwareVbusDetect { - fn is_usb_detected(&self) -> bool { - self.usb_detected.load(Ordering::Relaxed) - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - - if self.power_ready.load(Ordering::Relaxed) { - Poll::Ready(Ok(())) - } else if !self.usb_detected.load(Ordering::Relaxed) { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// USB driver. -pub struct Driver<'d, T: Instance, P: VbusDetect> { - _p: PeripheralRef<'d, T>, - alloc_in: Allocator, - alloc_out: Allocator, - usb_supply: P, -} - -impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { - /// Create a new USB driver. - pub fn new(usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, usb_supply: P) -> Self { - into_ref!(usb, irq); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _p: usb, - alloc_in: Allocator::new(), - alloc_out: Allocator::new(), - usb_supply, - } - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { let regs = T::regs(); if regs.events_usbreset.read().bits() != 0 { @@ -255,11 +86,40 @@ impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { } } -impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { +/// USB driver. +pub struct Driver<'d, T: Instance, V: VbusDetect> { + _p: PeripheralRef<'d, T>, + alloc_in: Allocator, + alloc_out: Allocator, + vbus_detect: V, +} + +impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { + /// Create a new USB driver. + pub fn new( + usb: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + vbus_detect: V, + ) -> Self { + into_ref!(usb); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + Self { + _p: usb, + alloc_in: Allocator::new(), + alloc_out: Allocator::new(), + vbus_detect, + } + } +} + +impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T, P>; + type Bus = Bus<'d, T, V>; fn alloc_endpoint_in( &mut self, @@ -298,7 +158,7 @@ impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P Bus { _p: unsafe { self._p.clone_unchecked() }, power_available: false, - usb_supply: self.usb_supply, + vbus_detect: self.vbus_detect, }, ControlPipe { _p: self._p, @@ -309,13 +169,13 @@ impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P } /// USB bus. -pub struct Bus<'d, T: Instance, P: VbusDetect> { +pub struct Bus<'d, T: Instance, V: VbusDetect> { _p: PeripheralRef<'d, T>, power_available: bool, - usb_supply: P, + vbus_detect: V, } -impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { +impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { async fn enable(&mut self) { let regs = T::regs(); @@ -347,7 +207,7 @@ impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { w }); - if self.usb_supply.wait_power_ready().await.is_ok() { + if self.vbus_detect.wait_power_ready().await.is_ok() { // Enable the USB pullup, allowing enumeration. regs.usbpullup.write(|w| w.connect().enabled()); trace!("enabled"); @@ -406,7 +266,7 @@ impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { trace!("USB event: ready"); } - if self.usb_supply.is_usb_detected() != self.power_available { + if self.vbus_detect.is_usb_detected() != self.power_available { self.power_available = !self.power_available; if self.power_available { trace!("Power event: available"); diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs new file mode 100644 index 00000000..cecd4c59 --- /dev/null +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -0,0 +1,177 @@ +//! Trait and implementations for performing VBUS detection. + +use core::future::poll_fn; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; + +use embassy_sync::waitqueue::AtomicWaker; + +use super::BUS_WAKER; +use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::pac; + +/// Trait for detecting USB VBUS power. +/// +/// There are multiple ways to detect USB power. The behavior +/// here provides a hook into determining whether it is. +pub trait VbusDetect { + /// Report whether power is detected. + /// + /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the + /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + fn is_usb_detected(&self) -> bool; + + /// Wait until USB power is ready. + /// + /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the + /// `USBPWRRDY` event from the `POWER` peripheral. + async fn wait_power_ready(&mut self) -> Result<(), ()>; +} + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegIrq = interrupt::POWER_CLOCK; +#[cfg(feature = "_nrf5340")] +type UsbRegIrq = interrupt::USBREGULATOR; + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegPeri = pac::POWER; +#[cfg(feature = "_nrf5340")] +type UsbRegPeri = pac::USBREGULATOR; + +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.events_usbdetected.read().bits() != 0 { + regs.events_usbdetected.reset(); + BUS_WAKER.wake(); + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + if regs.events_usbpwrrdy.read().bits() != 0 { + regs.events_usbpwrrdy.reset(); + POWER_WAKER.wake(); + } + } +} + +/// [`VbusDetect`] implementation using the native hardware POWER peripheral. +/// +/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces +/// to POWER. In that case, use [`VbusDetectSignal`]. +pub struct HardwareVbusDetect { + _private: (), +} + +static POWER_WAKER: AtomicWaker = AtomicWaker::new(); + +impl HardwareVbusDetect { + /// Create a new `VbusDetectNative`. + pub fn new(_irq: impl interrupt::Binding + 'static) -> Self { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + unsafe { UsbRegIrq::steal() }.unpend(); + unsafe { UsbRegIrq::steal() }.enable(); + + regs.intenset + .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); + + Self { _private: () } + } +} + +impl VbusDetect for HardwareVbusDetect { + fn is_usb_detected(&self) -> bool { + let regs = unsafe { &*UsbRegPeri::ptr() }; + regs.usbregstatus.read().vbusdetect().is_vbus_present() + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.usbregstatus.read().outputrdy().is_ready() { + Poll::Ready(Ok(())) + } else if !self.is_usb_detected() { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} + +/// Software-backed [`VbusDetect`] implementation. +/// +/// This implementation does not interact with the hardware, it allows user code +/// to notify the power events by calling functions instead. +/// +/// This is suitable for use with the nRF softdevice, by calling the functions +/// when the softdevice reports power-related events. +pub struct SoftwareVbusDetect { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +impl SoftwareVbusDetect { + /// Create a new `SoftwareVbusDetect`. + pub fn new(usb_detected: bool, power_ready: bool) -> Self { + BUS_WAKER.wake(); + + Self { + usb_detected: AtomicBool::new(usb_detected), + power_ready: AtomicBool::new(power_ready), + } + } + + /// Report whether power was detected. + /// + /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + pub fn detected(&self, detected: bool) { + self.usb_detected.store(detected, Ordering::Relaxed); + self.power_ready.store(false, Ordering::Relaxed); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + /// Report when USB power is ready. + /// + /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. + pub fn ready(&self) { + self.power_ready.store(true, Ordering::Relaxed); + POWER_WAKER.wake(); + } +} + +impl VbusDetect for &SoftwareVbusDetect { + fn is_usb_detected(&self) -> bool { + self.usb_detected.load(Ordering::Relaxed) + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + + if self.power_ready.load(Ordering::Relaxed) { + Poll::Ready(Ok(())) + } else if !self.usb_detected.load(Ordering::Relaxed) { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cfdda076..cc88d92c 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,7 +6,6 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -msos-descriptor = ["embassy-usb/msos-descriptor"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] @@ -17,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } @@ -36,7 +35,3 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } - -[[bin]] -name = "usb_serial_winusb" -required-features = ["msos-descriptor"] diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 083a1cbb..b8a72313 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -9,8 +9,9 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -19,6 +20,8 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; RNG => rng::InterruptHandler; }); @@ -60,9 +63,7 @@ async fn main(spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -86,6 +87,7 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Our MAC addr. diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 3d8a114c..7ccd2946 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -10,8 +10,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; @@ -20,6 +21,11 @@ use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + static SUSPENDED: AtomicBool = AtomicBool::new(false); #[embassy_executor::main] @@ -32,9 +38,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -50,6 +54,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; let mut device_handler = MyDeviceHandler::new(); @@ -62,6 +67,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index d7c9d55b..edf634a5 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; @@ -16,6 +17,11 @@ use embassy_usb::{Builder, Config}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +32,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -43,6 +47,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; @@ -54,6 +59,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 102d7ea6..9727a4f5 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -7,13 +7,19 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -24,9 +30,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -48,6 +52,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let mut state = State::new(); @@ -58,6 +63,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 558d4ba6..6da2c2a2 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -6,14 +6,29 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] @@ -39,10 +54,9 @@ async fn main(spawner: Spawner) { info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} + // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -59,34 +73,21 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 64], - serial_state: State<'static>, - } - static RESOURCES: StaticCell = StaticCell::new(); - let res = RESOURCES.init(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 64], - serial_state: State::new(), - }); + let state = singleton!(State::new()); // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Create classes on the builder. - let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); + let class = CdcAcmClass::new(&mut builder, state, 64); // Build the builder. let usb = builder.build(); diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 6561fc3b..6e4f71a4 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::msos::{self, windows_version}; @@ -16,6 +17,11 @@ use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + // This is a randomly generated GUID to allow clients on Windows to find our device const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; @@ -29,9 +35,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); From f5e09a8f4a6d7b1bd6723c60915cb9f29cf352f7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:40:53 +0100 Subject: [PATCH 17/17] nrf/interrupt: do not reexport `take!` macro. --- embassy-nrf/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ee29ce20..cb629902 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -100,7 +100,7 @@ mod chip; pub mod interrupt { //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::*; + pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; pub use crate::chip::irqs::*;