diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 145ba9c59..59c7a47ce 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,12 +3,12 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_cortex_m::interrupt::{Binding, Interrupt}; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_02::adc::{Channel, OneShot}; use crate::gpio::Pin; -use crate::interrupt::{self, InterruptExt}; +use crate::interrupt::{self, InterruptExt, ADC_IRQ_FIFO}; use crate::peripherals::ADC; use crate::{pac, peripherals, Peripheral}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -47,10 +47,9 @@ impl<'d> Adc<'d> { pub fn new( _inner: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding, _config: Config, ) -> Self { - into_ref!(irq); unsafe { let reset = Self::reset(); crate::reset::reset(reset); @@ -63,14 +62,10 @@ impl<'d> Adc<'d> { } // Setup IRQ - irq.disable(); - irq.set_handler(|_| unsafe { - let r = Self::regs(); - r.inte().write(|w| w.set_fifo(false)); - WAKER.wake(); - }); - irq.unpend(); - irq.enable(); + unsafe { + ADC_IRQ_FIFO::steal().unpend(); + ADC_IRQ_FIFO::steal().enable(); + }; Self { phantom: PhantomData } } @@ -165,6 +160,18 @@ macro_rules! impl_pin { }; } +pub struct InterruptHandler { + _empty: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = Adc::regs(); + r.inte().write(|w| w.set_fifo(false)); + WAKER.wake(); + } +} + impl_pin!(PIN_26, 0); impl_pin!(PIN_27, 1); impl_pin!(PIN_28, 2); diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d5dc94406..6ce77f073 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,7 +2,7 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; @@ -75,23 +75,21 @@ impl<'d, T: Instance> I2c<'d, T, Async> { peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, config: Config, ) -> Self { - into_ref!(scl, sda, irq); + into_ref!(scl, sda); let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); - irq.set_handler(Self::on_interrupt); unsafe { let i2c = T::regs(); // mask everything initially i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); } - irq.unpend(); - debug_assert!(!irq.is_pending()); - irq.enable(); i2c } @@ -115,14 +113,6 @@ impl<'d, T: Instance> I2c<'d, T, Async> { .await } - // Mask interrupts and wake any task waiting for this interrupt - unsafe fn on_interrupt(_: *mut ()) { - let i2c = T::regs(); - i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - - T::waker().wake(); - } - async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { if buffer.is_empty() { return Err(Error::InvalidReadBufferLength); @@ -320,6 +310,20 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt() { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + T::waker().wake(); + } +} + impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { fn new_inner( _peri: impl Peripheral

+ 'd, diff --git a/embassy-rp/src/interrupt.rs b/embassy-rp/src/interrupt.rs index 989f5dc2d..1db13deef 100644 --- a/embassy-rp/src/interrupt.rs +++ b/embassy-rp/src/interrupt.rs @@ -1,11 +1,7 @@ -//! Interrupt management -//! -//! This module implements an API for managing interrupts compatible with -//! nrf_softdevice::interrupt. Intended for switching between the two at compile-time. - -// Re-exports +//! Interrupt definitions and macros to bind them. +pub use cortex_m::interrupt::{CriticalSection, Mutex}; use embassy_cortex_m::interrupt::_export::declare; -pub use embassy_cortex_m::interrupt::*; +pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; use crate::pac::Interrupt as InterruptEnum; declare!(TIMER_IRQ_0); @@ -40,3 +36,30 @@ declare!(SWI_IRQ_2); declare!(SWI_IRQ_3); declare!(SWI_IRQ_4); declare!(SWI_IRQ_5); + +/// 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 {} + )* + )* + }; +} diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9d3de1bd8..12d6b8d91 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,7 +3,7 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; @@ -52,7 +52,7 @@ pub struct BufferedUartTx<'d, T: Instance> { } pub(crate) fn init_buffers<'d, T: Instance + 'd>( - irq: PeripheralRef<'d, T::Interrupt>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) { @@ -79,24 +79,23 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( w.set_rtim(true); w.set_txim(true); }); - }; - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); + }; } impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx, rx); + into_ref!(tx, rx); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), Some(rx.map_into()), None, None, config); init_buffers::(irq, tx_buffer, rx_buffer); @@ -109,7 +108,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new_with_rtscts( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, @@ -118,7 +117,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx, rx, cts, rts); + into_ref!(tx, rx, cts, rts); super::Uart::<'d, T, Async>::init( Some(tx.map_into()), @@ -163,12 +162,12 @@ impl<'d, T: Instance> BufferedUart<'d, T> { impl<'d, T: Instance> BufferedUartRx<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, rx: impl Peripheral

> + 'd, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, rx); + into_ref!(rx); super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), None, None, config); init_buffers::(irq, &mut [], rx_buffer); @@ -178,13 +177,13 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { pub fn new_with_rts( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, rx, rts); + into_ref!(rx, rts); super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), Some(rts.map_into()), None, config); init_buffers::(irq, &mut [], rx_buffer); @@ -312,12 +311,12 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx); + into_ref!(tx); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, None, config); init_buffers::(irq, tx_buffer, &mut []); @@ -327,13 +326,13 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn new_with_cts( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx, cts); + into_ref!(tx, cts); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, Some(cts.map_into()), config); init_buffers::(irq, tx_buffer, &mut []); @@ -482,97 +481,107 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { } } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::buffered_state(); +pub struct BufferedInterruptHandler { + _uart: PhantomData, +} - unsafe { - // Clear TX and error interrupt flags - // RX interrupt flags are cleared by reading from the FIFO. - let ris = r.uartris().read(); - r.uarticr().write(|w| { - w.set_txic(ris.txris()); - w.set_feic(ris.feris()); - w.set_peic(ris.peris()); - w.set_beic(ris.beris()); - w.set_oeic(ris.oeris()); - }); - - trace!("on_interrupt ris={:#X}", ris.0); - - // Errors - if ris.feris() { - warn!("Framing error"); - } - if ris.peris() { - warn!("Parity error"); - } - if ris.beris() { - warn!("Break error"); - } - if ris.oeris() { - warn!("Overrun error"); +impl interrupt::Handler for BufferedInterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + if r.uartdmacr().read().rxdmae() { + return; } - // RX - let mut rx_writer = s.rx_buf.writer(); - let rx_buf = rx_writer.push_slice(); - let mut n_read = 0; - let mut error = false; - for rx_byte in rx_buf { - if r.uartfr().read().rxfe() { - break; - } - let dr = r.uartdr().read(); - if (dr.0 >> 8) != 0 { - s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); - error = true; - // only fill the buffer with valid characters. the current character is fine - // if the error is an overrun, but if we add it to the buffer we'll report - // the overrun one character too late. drop it instead and pretend we were - // a bit slower at draining the rx fifo than we actually were. - // this is consistent with blocking uart error reporting. - break; - } - *rx_byte = dr.data(); - n_read += 1; - } - if n_read > 0 { - rx_writer.push_done(n_read); - s.rx_waker.wake(); - } else if error { - s.rx_waker.wake(); - } - // Disable any further RX interrupts when the buffer becomes full or - // errors have occurred. This lets us buffer additional errors in the - // fifo without needing more error storage locations, and most applications - // will want to do a full reset of their uart state anyway once an error - // has happened. - if s.rx_buf.is_full() || error { - r.uartimsc().write_clear(|w| { - w.set_rxim(true); - w.set_rtim(true); + let s = T::buffered_state(); + + unsafe { + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by reading from the FIFO. + let ris = r.uartris().read(); + r.uarticr().write(|w| { + w.set_txic(ris.txris()); + w.set_feic(ris.feris()); + w.set_peic(ris.peris()); + w.set_beic(ris.beris()); + w.set_oeic(ris.oeris()); }); - } - // TX - let mut tx_reader = s.tx_buf.reader(); - let tx_buf = tx_reader.pop_slice(); - let mut n_written = 0; - for tx_byte in tx_buf.iter_mut() { - if r.uartfr().read().txff() { - break; + trace!("on_interrupt ris={:#X}", ris.0); + + // Errors + if ris.feris() { + warn!("Framing error"); } - r.uartdr().write(|w| w.set_data(*tx_byte)); - n_written += 1; + if ris.peris() { + warn!("Parity error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + // RX + let mut rx_writer = s.rx_buf.writer(); + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + let mut error = false; + for rx_byte in rx_buf { + if r.uartfr().read().rxfe() { + break; + } + let dr = r.uartdr().read(); + if (dr.0 >> 8) != 0 { + s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); + error = true; + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + *rx_byte = dr.data(); + n_read += 1; + } + if n_read > 0 { + rx_writer.push_done(n_read); + s.rx_waker.wake(); + } else if error { + s.rx_waker.wake(); + } + // Disable any further RX interrupts when the buffer becomes full or + // errors have occurred. This lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if s.rx_buf.is_full() || error { + r.uartimsc().write_clear(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } + + // TX + let mut tx_reader = s.tx_buf.reader(); + let tx_buf = tx_reader.pop_slice(); + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; + } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; + } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } + // The TX interrupt only triggers once when the FIFO threshold is + // crossed. No need to disable it when the buffer becomes empty + // as it does re-trigger anymore once we have cleared it. } - if n_written > 0 { - tx_reader.pop_done(n_written); - s.tx_waker.wake(); - } - // The TX interrupt only triggers once when the FIFO threshold is - // crossed. No need to disable it when the buffer becomes empty - // as it does re-trigger anymore once we have cleared it. } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a0ee6b4ce..7234336b4 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; use embassy_futures::select::{select, Either}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -20,7 +20,7 @@ use crate::{pac, peripherals, Peripheral, RegExt}; #[cfg(feature = "nightly")] mod buffered; #[cfg(feature = "nightly")] -pub use buffered::{BufferedUart, BufferedUartRx, BufferedUartTx}; +pub use buffered::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, BufferedUartTx}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DataBits { @@ -203,11 +203,9 @@ impl<'d, T: Instance> UartTx<'d, T, Blocking> { #[cfg(feature = "nightly")] pub fn into_buffered( self, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx_buffer: &'d mut [u8], ) -> BufferedUartTx<'d, T> { - into_ref!(irq); - buffered::init_buffers::(irq, tx_buffer, &mut []); BufferedUartTx { phantom: PhantomData } @@ -235,25 +233,24 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { pub fn new( _uart: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(rx, irq, rx_dma); + into_ref!(rx, rx_dma); Uart::::init(None, Some(rx.map_into()), None, None, config); - Self::new_inner(Some(irq), Some(rx_dma.map_into())) + Self::new_inner(true, Some(rx_dma.map_into())) } - fn new_inner(irq: Option>, rx_dma: Option>) -> Self { - debug_assert_eq!(irq.is_some(), rx_dma.is_some()); - if let Some(irq) = irq { + fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { + debug_assert_eq!(has_irq, rx_dma.is_some()); + if has_irq { unsafe { // disable all error interrupts initially T::regs().uartimsc().write(|w| w.0 = 0); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); } - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); } Self { rx_dma, @@ -299,6 +296,12 @@ impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { if let Some(_) = self.rx_dma { unsafe { T::Interrupt::steal().disable(); + // clear dma flags. irq handlers use these to disambiguate among themselves. + T::regs().uartdmacr().write_clear(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + reg.set_dmaonerr(true); + }); } } } @@ -312,33 +315,41 @@ impl<'d, T: Instance> UartRx<'d, T, Blocking> { ) -> Self { into_ref!(rx); Uart::::init(None, Some(rx.map_into()), None, None, config); - Self::new_inner(None, None) + Self::new_inner(false, None) } #[cfg(feature = "nightly")] pub fn into_buffered( self, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, rx_buffer: &'d mut [u8], ) -> BufferedUartRx<'d, T> { - into_ref!(irq); - buffered::init_buffers::(irq, &mut [], rx_buffer); BufferedUartRx { phantom: PhantomData } } } -unsafe fn on_interrupt(_: *mut ()) { - let uart = T::regs(); - let state = T::dma_state(); - let errs = uart.uartris().read(); - state.rx_errs.store(errs.0 as u16, Ordering::Relaxed); - state.rx_err_waker.wake(); - // disable the error interrupts instead of clearing the flags. clearing the - // flags would allow the dma transfer to continue, potentially signaling - // completion before we can check for errors that happened *during* the transfer. - uart.uartimsc().write_clear(|w| w.0 = errs.0); +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let uart = T::regs(); + if !uart.uartdmacr().read().rxdmae() { + return; + } + + let state = T::dma_state(); + let errs = uart.uartris().read(); + state.rx_errs.store(errs.0 as u16, Ordering::Relaxed); + state.rx_err_waker.wake(); + // disable the error interrupts instead of clearing the flags. clearing the + // flags would allow the dma transfer to continue, potentially signaling + // completion before we can check for errors that happened *during* the transfer. + uart.uartimsc().write_clear(|w| w.0 = errs.0); + } } impl<'d, T: Instance> UartRx<'d, T, Async> { @@ -428,7 +439,17 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(tx, rx); - Self::new_inner(uart, tx.map_into(), rx.map_into(), None, None, None, None, None, config) + Self::new_inner( + uart, + tx.map_into(), + rx.map_into(), + None, + None, + false, + None, + None, + config, + ) } /// Create a new UART with hardware flow control (RTS/CTS) @@ -447,7 +468,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), - None, + false, None, None, config, @@ -457,12 +478,10 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { #[cfg(feature = "nightly")] pub fn into_buffered( self, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) -> BufferedUart<'d, T> { - into_ref!(irq); - buffered::init_buffers::(irq, tx_buffer, rx_buffer); BufferedUart { @@ -478,19 +497,19 @@ impl<'d, T: Instance> Uart<'d, T, Async> { uart: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(tx, rx, irq, tx_dma, rx_dma); + into_ref!(tx, rx, tx_dma, rx_dma); Self::new_inner( uart, tx.map_into(), rx.map_into(), None, None, - Some(irq), + true, Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, @@ -504,19 +523,19 @@ impl<'d, T: Instance> Uart<'d, T, Async> { rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(tx, rx, cts, rts, irq, tx_dma, rx_dma); + into_ref!(tx, rx, cts, rts, tx_dma, rx_dma); Self::new_inner( uart, tx.map_into(), rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), - Some(irq), + true, Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, @@ -531,7 +550,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { mut rx: PeripheralRef<'d, AnyPin>, mut rts: Option>, mut cts: Option>, - irq: Option>, + has_irq: bool, tx_dma: Option>, rx_dma: Option>, config: Config, @@ -546,7 +565,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { Self { tx: UartTx::new_inner(tx_dma), - rx: UartRx::new_inner(irq, rx_dma), + rx: UartRx::new_inner(has_irq, rx_dma), } } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index a049e4769..fada2790f 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -4,7 +4,7 @@ use core::slice; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_cortex_m::interrupt::{self, Binding}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{ @@ -105,11 +105,11 @@ pub struct Driver<'d, T: Instance> { } impl<'d, T: Instance> Driver<'d, T> { - pub fn new(_usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + pub fn new(_usb: impl Peripheral

+ 'd, _irq: impl Binding>) -> Self { + unsafe { + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); + } let regs = T::regs(); unsafe { @@ -149,47 +149,6 @@ impl<'d, T: Instance> Driver<'d, T> { } } - fn on_interrupt(_: *mut ()) { - unsafe { - let regs = T::regs(); - //let x = regs.istr().read().0; - //trace!("USB IRQ: {:08x}", x); - - let ints = regs.ints().read(); - - if ints.bus_reset() { - regs.inte().write_clear(|w| w.set_bus_reset(true)); - BUS_WAKER.wake(); - } - if ints.dev_resume_from_host() { - regs.inte().write_clear(|w| w.set_dev_resume_from_host(true)); - BUS_WAKER.wake(); - } - if ints.dev_suspend() { - regs.inte().write_clear(|w| w.set_dev_suspend(true)); - BUS_WAKER.wake(); - } - if ints.setup_req() { - regs.inte().write_clear(|w| w.set_setup_req(true)); - EP_OUT_WAKERS[0].wake(); - } - - if ints.buff_status() { - let s = regs.buff_status().read(); - regs.buff_status().write_value(s); - - for i in 0..EP_COUNT { - if s.ep_in(i) { - EP_IN_WAKERS[i].wake(); - } - if s.ep_out(i) { - EP_OUT_WAKERS[i].wake(); - } - } - } - } - } - fn alloc_endpoint( &mut self, ep_type: EndpointType, @@ -288,6 +247,51 @@ impl<'d, T: Instance> Driver<'d, T> { } } +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + //let x = regs.istr().read().0; + //trace!("USB IRQ: {:08x}", x); + + let ints = regs.ints().read(); + + if ints.bus_reset() { + regs.inte().write_clear(|w| w.set_bus_reset(true)); + BUS_WAKER.wake(); + } + if ints.dev_resume_from_host() { + regs.inte().write_clear(|w| w.set_dev_resume_from_host(true)); + BUS_WAKER.wake(); + } + if ints.dev_suspend() { + regs.inte().write_clear(|w| w.set_dev_suspend(true)); + BUS_WAKER.wake(); + } + if ints.setup_req() { + regs.inte().write_clear(|w| w.set_setup_req(true)); + EP_OUT_WAKERS[0].wake(); + } + + if ints.buff_status() { + let s = regs.buff_status().read(); + regs.buff_status().write_value(s); + + for i in 0..EP_COUNT { + if s.ep_in(i) { + EP_IN_WAKERS[i].wake(); + } + if s.ep_out(i) { + EP_OUT_WAKERS[i].wake(); + } + } + } + } +} + impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 4202fd394..7c2ca19f7 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -4,16 +4,19 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config}; -use embassy_rp::interrupt; +use embassy_rp::adc::{Adc, Config, InterruptHandler}; +use embassy_rp::bind_interrupts; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ADC_IRQ_FIFO => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let irq = interrupt::take!(ADC_IRQ_FIFO); - let mut adc = Adc::new(p.ADC, irq, Config::default()); + let mut adc = Adc::new(p.ADC, Irqs, Config::default()); let mut p26 = p.PIN_26; let mut p27 = p.PIN_27; diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs index d1a2e3cd7..cf3cf742c 100644 --- a/examples/rp/src/bin/i2c_async.rs +++ b/examples/rp/src/bin/i2c_async.rs @@ -4,12 +4,17 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::i2c::{self, Config}; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; +use embassy_rp::i2c::{self, Config, InterruptHandler}; +use embassy_rp::peripherals::I2C1; use embassy_time::{Duration, Timer}; use embedded_hal_async::i2c::I2c; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + I2C1_IRQ => InterruptHandler; +}); + #[allow(dead_code)] mod mcp23017 { pub const ADDR: u8 = 0x20; // default addr @@ -64,10 +69,9 @@ async fn main(_spawner: Spawner) { let sda = p.PIN_14; let scl = p.PIN_15; - let irq = interrupt::take!(I2C1_IRQ); info!("set up i2c "); - let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default()); + let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default()); use mcp23017::*; diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index a8a682274..d6f01b4de 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -5,13 +5,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_executor::_export::StaticCell; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; -use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config}; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => BufferedInterruptHandler; +}); + macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; @@ -26,10 +30,9 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); - let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut singleton!([0u8; 16])[..]; let rx_buf = &mut singleton!([0u8; 16])[..]; - let uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); + let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); let (rx, mut tx) = uart.split(); unwrap!(spawner.spawn(reader(rx))); diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index 4119a309f..c0943a1b8 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -7,24 +7,22 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART1; -use embassy_rp::uart::{Async, Config, UartRx, UartTx}; +use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART1_IRQ => InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); - let uart_rx = UartRx::new( - p.UART1, - p.PIN_5, - interrupt::take!(UART1_IRQ), - p.DMA_CH1, - Config::default(), - ); + let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default()); unwrap!(spawner.spawn(reader(uart_rx))); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 66a6ed4d0..2ddfb6344 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -6,8 +6,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; -use embassy_rp::usb::Driver; -use embassy_rp::{interrupt, peripherals}; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_rp::{bind_interrupts, peripherals}; 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}; @@ -15,6 +16,10 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + type MyDriver = Driver<'static, peripherals::USB>; macro_rules! singleton { @@ -48,8 +53,7 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. - let irq = interrupt::take!(USBCTRL_IRQ); - let driver = Driver::new(p.USB, irq); + let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs index 52417a02e..7c90d0ca3 100644 --- a/examples/rp/src/bin/usb_logger.rs +++ b/examples/rp/src/bin/usb_logger.rs @@ -3,12 +3,16 @@ #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::USB; -use embassy_rp::usb::Driver; +use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + #[embassy_executor::task] async fn logger_task(driver: Driver<'static, USB>) { embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); @@ -17,8 +21,7 @@ async fn logger_task(driver: Driver<'static, USB>) { #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let irq = interrupt::take!(USBCTRL_IRQ); - let driver = Driver::new(p.USB, irq); + let driver = Driver::new(p.USB, Irqs); spawner.spawn(logger_task(driver)).unwrap(); let mut counter = 0; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 8160a1875..ca728536c 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -5,13 +5,18 @@ use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::interrupt; -use embassy_rp::usb::{Driver, Instance}; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 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 { + USBCTRL_IRQ => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello there!"); @@ -19,8 +24,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. - let irq = interrupt::take!(USBCTRL_IRQ); - let driver = Driver::new(p.USB, irq); + let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 1deb22ce6..1dcf57d07 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -4,13 +4,18 @@ use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::interrupt; -use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => BufferedInterruptHandler; +}); + async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Result<[u8; N], Error> { let mut buf = [255; N]; match uart.read_exact(&mut buf).await { @@ -60,13 +65,12 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); - let mut irq = interrupt::take!(UART0_IRQ); { let config = Config::default(); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + let mut uart = BufferedUart::new(&mut uart, Irqs, &mut tx, &mut rx, tx_buf, rx_buf, config); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart. @@ -86,7 +90,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + let mut uart = BufferedUart::new(&mut uart, Irqs, &mut tx, &mut rx, tx_buf, rx_buf, config); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart. @@ -121,7 +125,7 @@ async fn main(_spawner: Spawner) { config.baudrate = 1000; let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + let mut uart = BufferedUart::new(&mut uart, Irqs, &mut tx, &mut rx, tx_buf, rx_buf, config); // break on empty buffer uart.send_break(20).await; @@ -155,7 +159,7 @@ async fn main(_spawner: Spawner) { config.baudrate = 1000; config.parity = Parity::ParityEven; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config); + let mut uart = BufferedUartRx::new(&mut uart, Irqs, &mut rx, rx_buf, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) { send(pin, v, Some(parity != 0)).await; @@ -202,7 +206,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 1000; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config); + let mut uart = BufferedUartRx::new(&mut uart, Irqs, &mut rx, rx_buf, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { if good { diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 52f42e582..75be76eda 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -4,12 +4,17 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::interrupt; -use embassy_rp::uart::{Async, Config, Error, Instance, Parity, Uart, UartRx}; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{Async, Config, Error, Instance, InterruptHandler, Parity, Uart, UartRx}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => InterruptHandler; +}); + async fn read(uart: &mut Uart<'_, impl Instance, Async>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.read(&mut buf).await?; @@ -51,7 +56,6 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); - let mut irq = interrupt::take!(UART0_IRQ); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. @@ -61,7 +65,7 @@ async fn main(_spawner: Spawner) { &mut uart, &mut tx, &mut rx, - &mut irq, + Irqs, &mut p.DMA_CH0, &mut p.DMA_CH1, config, @@ -82,7 +86,7 @@ async fn main(_spawner: Spawner) { &mut uart, &mut tx, &mut rx, - &mut irq, + Irqs, &mut p.DMA_CH0, &mut p.DMA_CH1, config, @@ -111,7 +115,7 @@ async fn main(_spawner: Spawner) { &mut uart, &mut tx, &mut rx, - &mut irq, + Irqs, &mut p.DMA_CH0, &mut p.DMA_CH1, config, @@ -154,7 +158,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 1000; config.parity = Parity::ParityEven; - let mut uart = UartRx::new(&mut uart, &mut rx, &mut irq, &mut p.DMA_CH0, config); + let mut uart = UartRx::new(&mut uart, &mut rx, Irqs, &mut p.DMA_CH0, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) { send(pin, v, Some(parity != 0)).await; @@ -199,7 +203,7 @@ async fn main(_spawner: Spawner) { // choose a very slow baud rate to make tests reliable even with O0 let mut config = Config::default(); config.baudrate = 1000; - let mut uart = UartRx::new(&mut uart, &mut rx, &mut irq, &mut p.DMA_CH0, config); + let mut uart = UartRx::new(&mut uart, &mut rx, Irqs, &mut p.DMA_CH0, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { if good { diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index d8c9aecf6..8605bb1c5 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -4,11 +4,16 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_rp::interrupt; -use embassy_rp::uart::{Config, Uart}; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{BufferedInterruptHandler, Config, Uart}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => BufferedInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -29,11 +34,10 @@ async fn main(_spawner: Spawner) { uart.blocking_read(&mut buf).unwrap(); assert_eq!(buf, data); - let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = uart.into_buffered(irq, tx_buf, rx_buf); + let mut uart = uart.into_buffered(Irqs, tx_buf, rx_buf); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart.