From 9611e7c9f2609271af2ead7e91e6ee918d3dadd3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:05:12 +0200 Subject: [PATCH 01/62] Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor --- embassy-rp/Cargo.toml | 45 ++-- embassy-rp/src/lib.rs | 2 + embassy-rp/src/uart/buffered.rs | 286 ++++++++++++++++++++++++ embassy-rp/src/{uart.rs => uart/mod.rs} | 84 ++++++- 4 files changed, 401 insertions(+), 16 deletions(-) create mode 100644 embassy-rp/src/uart/buffered.rs rename embassy-rp/src/{uart.rs => uart/mod.rs} (87%) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..92780ee39 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -7,9 +7,7 @@ edition = "2021" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] -flavors = [ - { name = "rp2040", target = "thumbv6m-none-eabi" }, -] +flavors = [{ name = "rp2040", target = "thumbv6m-none-eabi" }] [features] defmt = ["dep:defmt", "embassy-usb?/defmt"] @@ -20,8 +18,16 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] +time-driver = [] + # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] +nightly = [ + "embassy-executor/nightly", + "embedded-hal-1", + "embedded-hal-async", + "embassy-embedded-hal/nightly", + "dep:embassy-usb", +] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -30,11 +36,15 @@ unstable-traits = ["embedded-hal-1"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ + "tick-hz-1_000_000", +] } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = [ + "prio-bits-2", +] } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-usb = { version = "0.1.0", path = "../embassy-usb", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -43,11 +53,18 @@ cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" -futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", +] } +embedded-io = { version = "0.3.0", features = ["async"], optional = true } -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } +rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev = "017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = [ + "rt", +] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } -embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ + "unproven", +] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true } +embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..8dcefece2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; +#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -108,6 +109,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + #[cfg(feature = "time-driver")] timer::init(); dma::init(); } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs new file mode 100644 index 000000000..c31af8018 --- /dev/null +++ b/embassy-rp/src/uart/buffered.rs @@ -0,0 +1,286 @@ +use core::future::Future; +use core::task::Poll; + +use atomic_polyfill::{compiler_fence, Ordering}; +use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_hal_common::ring_buffer::RingBuffer; +use embassy_sync::waitqueue::WakerRegistration; +use futures::future::poll_fn; + +use super::*; + +pub struct State<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> State<'d, T> { + pub fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct StateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + rx_waker: WakerRegistration, + rx: RingBuffer<'d>, + + tx_waker: WakerRegistration, + tx: RingBuffer<'d>, +} + +unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} + +pub struct BufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, StateInner<'d, T>>, +} + +impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} + +impl<'d, T: Instance> BufferedUart<'d, T> { + pub fn new( + state: &'d mut State<'d, T>, + _uart: Uart<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + ) -> BufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + phantom: PhantomData, + tx: RingBuffer::new(tx_buffer), + tx_waker: WakerRegistration::new(), + + rx: RingBuffer::new(rx_buffer), + rx_waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> StateInner<'d, T> +where + Self: 'd, +{ + fn on_rx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.rxris() { + if ris.peris() { + warn!("Parity error"); + } + if ris.feris() { + warn!("Framing error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + let buf = self.rx.push_buf(); + if !buf.is_empty() { + buf[0] = r.uartdr().read().data(); + self.rx.push(1); + } else { + warn!("RX buffer full, discard received byte"); + } + + if self.rx.is_full() { + self.rx_waker.wake(); + } + } + + if ris.rtris() { + self.rx_waker.wake(); + }; + } + } + + fn on_tx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rtic(true); + }); + + if ris.txris() { + let buf = self.tx.pop_buf(); + if !buf.is_empty() { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + r.uartdr().write(|w| w.set_data(buf[0].into())); + self.tx.pop(1); + self.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); + } + } + } + } +} + +impl<'d, T: Instance> PeripheralState for StateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.on_rx(); + self.on_tx(); + } +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.rx.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let (poll, empty) = self.inner.with(|state| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart/mod.rs similarity index 87% rename from embassy-rp/src/uart.rs rename to embassy-rp/src/uart/mod.rs index 987b716b4..3b71d87be 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart/mod.rs @@ -475,6 +475,76 @@ mod eh1 { impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + } #[cfg(all( @@ -532,6 +602,12 @@ mod eha { } } +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::*; + + mod sealed { use super::*; @@ -541,6 +617,8 @@ mod sealed { const TX_DREQ: u8; const RX_DREQ: u8; + type Interrupt: crate::interrupt::Interrupt; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} @@ -571,6 +649,8 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { pac::$inst @@ -580,8 +660,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0, 20, 21); -impl_instance!(UART1, UART1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} From 31d85da78a06cdee6ec037d73b12537d3c906725 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:36:27 +0200 Subject: [PATCH 02/62] Add bufferedUart, including a split version for only Rx or Tx --- embassy-rp/src/uart/buffered.rs | 379 ++++++++++++++++++++++++++------ embassy-rp/src/uart/mod.rs | 2 +- 2 files changed, 315 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c31af8018..3eb96e3d5 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -9,31 +9,70 @@ use futures::future::poll_fn; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); +pub struct State<'d, T: Instance>(StateStorage>); impl<'d, T: Instance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } -struct StateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, +pub struct RxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> RxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } } -unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} +pub struct TxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> TxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct RxStateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + waker: WakerRegistration, + buf: RingBuffer<'d>, +} + +struct TxStateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + waker: WakerRegistration, + buf: RingBuffer<'d>, +} + +struct FullStateInner<'d, T: Instance> { + rx: RxStateInner<'d, T>, + tx: TxStateInner<'d, T>, +} + +unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {} + +unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {} + +unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {} pub struct BufferedUart<'d, T: Instance> { - inner: PeripheralMutex<'d, StateInner<'d, T>>, + inner: PeripheralMutex<'d, FullStateInner<'d, T>>, +} + +pub struct RxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, RxStateInner<'d, T>>, +} + +pub struct TxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for TxBufferedUart<'d, T> {} impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( @@ -55,66 +94,158 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), - - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), + inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner { + tx: TxStateInner { + phantom: PhantomData, + waker: WakerRegistration::new(), + buf: RingBuffer::new(tx_buffer), + }, + rx: RxStateInner { + phantom: PhantomData, + waker: WakerRegistration::new(), + buf: RingBuffer::new(rx_buffer), + }, }), } } } -impl<'d, T: Instance> StateInner<'d, T> +impl<'d, T: Instance> RxBufferedUart<'d, T> { + pub fn new( + state: &'d mut RxState<'d, T>, + _uart: UartRx<'d, T, M>, + irq: impl Peripheral

+ 'd, + rx_buffer: &'d mut [u8], + ) -> RxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(rx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> TxBufferedUart<'d, T> { + pub fn new( + state: &'d mut TxState<'d, T>, + _uart: UartTx<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + ) -> TxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(tx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> where Self: 'd, { - fn on_rx(&mut self) { + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.rx.on_interrupt(); + self.tx.on_interrupt(); + } +} + +impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartris().read(); + let ris = r.uartmis().read(); // Clear interrupt flags - r.uarticr().write(|w| { + r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxris() { - if ris.peris() { + if ris.rxmis() { + if ris.pemis() { warn!("Parity error"); + r.uarticr().modify(|w| { + w.set_peic(true); + }); } - if ris.feris() { + if ris.femis() { warn!("Framing error"); + r.uarticr().modify(|w| { + w.set_feic(true); + }); } - if ris.beris() { + if ris.bemis() { warn!("Break error"); + r.uarticr().modify(|w| { + w.set_beic(true); + }); } - if ris.oeris() { + if ris.oemis() { warn!("Overrun error"); + r.uarticr().modify(|w| { + w.set_oeic(true); + }); } - let buf = self.rx.push_buf(); + let buf = self.buf.push_buf(); if !buf.is_empty() { buf[0] = r.uartdr().read().data(); - self.rx.push(1); + self.buf.push(1); } else { warn!("RX buffer full, discard received byte"); } - if self.rx.is_full() { - self.rx_waker.wake(); + if self.buf.is_full() { + self.waker.wake(); } } - if ris.rtris() { - self.rx_waker.wake(); + if ris.rtmis() { + self.waker.wake(); }; } } +} - fn on_tx(&mut self) { +impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { let r = T::regs(); unsafe { let ris = r.uartris().read(); @@ -124,14 +255,14 @@ where }); if ris.txris() { - let buf = self.tx.pop_buf(); + let buf = self.buf.pop_buf(); if !buf.is_empty() { r.uartimsc().modify(|w| { w.set_txim(true); }); r.uartdr().write(|w| w.set_data(buf[0].into())); - self.tx.pop(1); - self.tx_waker.wake(); + self.buf.pop(1); + self.waker.wake(); } else { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { @@ -143,17 +274,6 @@ where } } -impl<'d, T: Instance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); - } -} - impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other @@ -164,8 +284,16 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { + type Error = Error; +} + impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> where Self: 'a; @@ -176,20 +304,58 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); + let data = state.rx.buf.pop_buf(); if !data.is_empty() { let len = data.len().min(buf.len()); buf[..len].copy_from_slice(&data[..len]); - if state.rx.is_full() { + if state.rx.buf.is_full() { do_pend = true; } - state.rx.pop(len); + state.rx.buf.pop(len); return Poll::Ready(Ok(len)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.buf.is_full() { + do_pend = true; + } + state.buf.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.waker.register(cx.waker()); Poll::Pending }); @@ -213,7 +379,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); + let buf = state.rx.buf.pop_buf(); if !buf.is_empty() { let buf: &[u8] = buf; // Safety: buffer lives as long as uart @@ -221,7 +387,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> return Poll::Ready(Ok(buf)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); Poll::>::Pending }) }) @@ -229,8 +395,45 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> fn consume(&mut self, amt: usize) { let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); + let full = state.rx.buf.is_full(); + state.rx.buf.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.buf.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.buf.is_full(); + state.buf.pop(amt); full }); if signal { @@ -247,16 +450,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); + let empty = state.tx.buf.is_empty(); + let tx_buf = state.tx.buf.push_buf(); if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); + state.tx.waker.register(cx.waker()); return (Poll::Pending, empty); } let n = core::cmp::min(tx_buf.len(), buf.len()); tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + state.tx.buf.push(n); (Poll::Ready(Ok(n)), empty) }); @@ -274,8 +477,54 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { poll_fn(move |cx| { self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); + if !state.tx.buf.is_empty() { + state.tx.waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let (poll, empty) = self.inner.with(|state| { + let empty = state.buf.is_empty(); + let tx_buf = state.buf.push_buf(); + if tx_buf.is_empty() { + state.waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.buf.is_empty() { + state.waker.register(cx.waker()); return Poll::Pending; } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 3b71d87be..67e24b605 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(true); + w.set_fen(false); }); r.uartcr().write(|w| { From b2d0f8d5903f868277732b4b12365945783d1720 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:49:47 +0200 Subject: [PATCH 03/62] Formatting --- embassy-rp/src/uart/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 67e24b605..76ecdf7ac 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -544,7 +544,6 @@ mod eh1 { self.blocking_flush().map_err(nb::Error::Other) } } - } #[cfg(all( @@ -607,7 +606,6 @@ mod buffered; #[cfg(feature = "nightly")] pub use buffered::*; - mod sealed { use super::*; @@ -649,7 +647,7 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { From c4d5c047d7311e506ee1c0c9322cbf4e72dd6bcc Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Thu, 15 Sep 2022 12:34:17 +0200 Subject: [PATCH 04/62] make `State::new()` const, consistent with others --- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 1ab0438ad..38629a932 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -29,7 +29,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d67c3c5e4..a81ee1183 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -19,7 +19,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index a7fa43894..5f6dabb3b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -11,7 +11,7 @@ use super::*; pub struct State<'d, T: BasicInstance>(StateStorage>); impl<'d, T: BasicInstance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } From 70a3b85acc3b87abab5a66b1a02da033789b5e1a Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:32:43 +0200 Subject: [PATCH 05/62] Add .into_inner() and .get_mut() to Mutex --- embassy-sync/src/mutex.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 75a6e8dd3..a792cf070 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -111,6 +111,20 @@ where Ok(MutexGuard { mutex: self }) } + + /// Consumes this mutex, returning the underlying data. + pub fn into_inner(self) -> T + where T: Sized { + self.inner.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the Mutex mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() + } } /// Async mutex guard. From 79654510b71290632ee659dd2ae1851f33f48374 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:44:33 +0200 Subject: [PATCH 06/62] Make rustfmt happy --- embassy-sync/src/mutex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index a792cf070..92101c6b5 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -114,7 +114,9 @@ where /// Consumes this mutex, returning the underlying data. pub fn into_inner(self) -> T - where T: Sized { + where + T: Sized, + { self.inner.into_inner() } From f7267d493fe9ed63b02f82267d29a10a4b1cf515 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:45:03 +0200 Subject: [PATCH 07/62] Feature-gate time-driver in embassy-rp --- ci.sh | 2 +- embassy-rp/Cargo.toml | 2 ++ embassy-rp/src/lib.rs | 2 ++ examples/rp/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index fa24d5268..ae1b44281 100755 --- a/ci.sh +++ b/ci.sh @@ -120,7 +120,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..1e26f7dd2 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -20,6 +20,8 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] +time-driver = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..8dcefece2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; +#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -108,6 +109,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + #[cfg(feature = "time-driver")] timer::init(); dma::init(); } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 18a92b094..17393322c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } From feead3ae89d57e9f0ff7d7a264136d3e89aaebcf Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 06:45:27 +0200 Subject: [PATCH 08/62] Implement RealTimeClock for embassy-rp --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/lib.rs | 3 + embassy-rp/src/rtc/datetime_chrono.rs | 62 ++++++++ embassy-rp/src/rtc/datetime_no_deps.rs | 127 +++++++++++++++++ embassy-rp/src/rtc/filter.rs | 100 +++++++++++++ embassy-rp/src/rtc/mod.rs | 188 +++++++++++++++++++++++++ 7 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/rtc/datetime_chrono.rs create mode 100644 embassy-rp/src/rtc/datetime_no_deps.rs create mode 100644 embassy-rp/src/rtc/filter.rs create mode 100644 embassy-rp/src/rtc/mod.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..bc6f44662 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -44,6 +44,7 @@ cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +chrono = { version = "0.4", default-features = false, optional = true } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 3ad1e5d82..1c446f389 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 { 125_000_000 } -pub(crate) fn _clk_rtc_freq() -> u32 { +pub(crate) fn clk_rtc_freq() -> u32 { 46875 } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..730354557 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rtc; pub mod spi; pub mod timer; pub mod uart; @@ -84,6 +85,8 @@ embassy_hal_common::peripherals! { DMA_CH11, USB, + + RTC, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rtc/datetime_chrono.rs b/embassy-rp/src/rtc/datetime_chrono.rs new file mode 100644 index 000000000..b3c78dd47 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_chrono.rs @@ -0,0 +1,62 @@ +use chrono::{Datelike, Timelike}; + +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Alias for [`chrono::NaiveDateTime`] +pub type DateTime = chrono::NaiveDateTime; +/// Alias for [`chrono::Weekday`] +pub type DayOfWeek = chrono::Weekday; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] has an invalid year. The year must be between 0 and 4095. + InvalidYear, + /// The [DateTime] contains an invalid date. + InvalidDate, + /// The [DateTime] contains an invalid time. + InvalidTime, +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw.num_days_from_sunday() as u8 +} + +pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year() < 0 || dt.year() > 4095 { + // rp2040 can't hold these years + Err(Error::InvalidYear) + } else { + // The rest of the chrono date is assumed to be valid + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year() as u16); + w.set_month(dt.month() as u8); + w.set_day(dt.day() as u8); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.weekday().num_days_from_sunday() as u8); + w.set_hour(dt.hour() as u8); + w.set_min(dt.minute() as u8); + w.set_sec(dt.second() as u8); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year() as i32; + let month = rtc_1.month() as u32; + let day = rtc_1.day() as u32; + + let hour = rtc_0.hour() as u32; + let minute = rtc_0.min() as u32; + let second = rtc_0.sec() as u32; + + let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?; + let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?; + Ok(DateTime::new(date, time)) +} diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs new file mode 100644 index 000000000..92770e984 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_no_deps.rs @@ -0,0 +1,127 @@ +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. + InvalidYear, + /// The [DateTime] contains an invalid month value. Must be between `1..=12`. + InvalidMonth, + /// The [DateTime] contains an invalid day value. Must be between `1..=31`. + InvalidDay, + /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday. + InvalidDayOfWeek( + /// The value of the DayOfWeek that was given. + u8, + ), + /// The [DateTime] contains an invalid hour value. Must be between `0..=23`. + InvalidHour, + /// The [DateTime] contains an invalid minute value. Must be between `0..=59`. + InvalidMinute, + /// The [DateTime] contains an invalid second value. Must be between `0..=59`. + InvalidSecond, +} + +/// Structure containing date and time information +pub struct DateTime { + /// 0..4095 + pub year: u16, + /// 1..12, 1 is January + pub month: u8, + /// 1..28,29,30,31 depending on month + pub day: u8, + /// + pub day_of_week: DayOfWeek, + /// 0..23 + pub hour: u8, + /// 0..59 + pub minute: u8, + /// 0..59 + pub second: u8, +} + +/// A day of the week +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[allow(missing_docs)] +pub enum DayOfWeek { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, +} + +fn day_of_week_from_u8(v: u8) -> Result { + Ok(match v { + 0 => DayOfWeek::Sunday, + 1 => DayOfWeek::Monday, + 2 => DayOfWeek::Tuesday, + 3 => DayOfWeek::Wednesday, + 4 => DayOfWeek::Thursday, + 5 => DayOfWeek::Friday, + 6 => DayOfWeek::Saturday, + x => return Err(Error::InvalidDayOfWeek(x)), + }) +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw as u8 +} + +pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year > 4095 { + Err(Error::InvalidYear) + } else if dt.month < 1 || dt.month > 12 { + Err(Error::InvalidMonth) + } else if dt.day < 1 || dt.day > 31 { + Err(Error::InvalidDay) + } else if dt.hour > 23 { + Err(Error::InvalidHour) + } else if dt.minute > 59 { + Err(Error::InvalidMinute) + } else if dt.second > 59 { + Err(Error::InvalidSecond) + } else { + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year); + w.set_month(dt.month); + w.set_day(dt.day); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.day_of_week as u8); + w.set_hour(dt.hour); + w.set_min(dt.minute); + w.set_sec(dt.second); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year(); + let month = rtc_1.month(); + let day = rtc_1.day(); + + let day_of_week = rtc_0.dotw(); + let hour = rtc_0.hour(); + let minute = rtc_0.min(); + let second = rtc_0.sec(); + + let day_of_week = day_of_week_from_u8(day_of_week)?; + Ok(DateTime { + year, + month, + day, + day_of_week, + hour, + minute, + second, + }) +} diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs new file mode 100644 index 000000000..d4a3bab2f --- /dev/null +++ b/embassy-rp/src/rtc/filter.rs @@ -0,0 +1,100 @@ +use super::DayOfWeek; +use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1}; + +/// A filter used for [`RealTimeClock::schedule_alarm`]. +/// +/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm +#[derive(Default)] +pub struct DateTimeFilter { + /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. + pub year: Option, + /// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value. + pub month: Option, + /// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value. + pub day: Option, + /// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value. + pub day_of_week: Option, + /// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value. + pub hour: Option, + /// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value. + pub minute: Option, + /// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value. + pub second: Option, +} + +impl DateTimeFilter { + /// Set a filter on the given year + pub fn year(mut self, year: u16) -> Self { + self.year = Some(year); + self + } + /// Set a filter on the given month + pub fn month(mut self, month: u8) -> Self { + self.month = Some(month); + self + } + /// Set a filter on the given day + pub fn day(mut self, day: u8) -> Self { + self.day = Some(day); + self + } + /// Set a filter on the given day of the week + pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self { + self.day_of_week = Some(day_of_week); + self + } + /// Set a filter on the given hour + pub fn hour(mut self, hour: u8) -> Self { + self.hour = Some(hour); + self + } + /// Set a filter on the given minute + pub fn minute(mut self, minute: u8) -> Self { + self.minute = Some(minute); + self + } + /// Set a filter on the given second + pub fn second(mut self, second: u8) -> Self { + self.second = Some(second); + self + } +} + +// register helper functions +impl DateTimeFilter { + pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) { + if let Some(year) = self.year { + w.set_year_ena(true); + + w.set_year(year); + } + if let Some(month) = self.month { + w.set_month_ena(true); + w.set_month(month); + } + if let Some(day) = self.day { + w.set_day_ena(true); + w.set_day(day); + } + } + pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) { + if let Some(day_of_week) = self.day_of_week { + w.set_dotw_ena(true); + let bits = super::datetime::day_of_week_to_u8(day_of_week); + + w.set_dotw(bits); + } + if let Some(hour) = self.hour { + w.set_hour_ena(true); + w.set_hour(hour); + } + if let Some(minute) = self.minute { + w.set_min_ena(true); + w.set_min(minute); + } + if let Some(second) = self.second { + w.set_sec_ena(true); + w.set_sec(second); + } + } +} diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs new file mode 100644 index 000000000..7f3bbbe73 --- /dev/null +++ b/embassy-rp/src/rtc/mod.rs @@ -0,0 +1,188 @@ +mod filter; + +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; + +pub use self::filter::DateTimeFilter; + +#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")] +#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")] +mod datetime; + +pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +use crate::clocks::clk_rtc_freq; + +/// A reference to the real time clock of the system +pub struct RealTimeClock<'d, T: Instance> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> RealTimeClock<'d, T> { + /// Create a new instance of the real time clock, with the given date as an initial value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn new(inner: impl Peripheral

+ 'd, initial_date: DateTime) -> Result { + into_ref!(inner); + + // Set the RTC divider + unsafe { + inner + .regs() + .clkdiv_m1() + .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) + }; + + let mut result = Self { inner }; + result.set_leap_year_check(true); // should be on by default, make sure this is the case. + result.set_datetime(initial_date)?; + Ok(result) + } + + /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. + /// + /// Leap year checking is enabled by default. + pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { + unsafe { + self.inner + .regs() + .ctrl() + .modify(|w| w.set_force_notleapyear(!leap_year_check_enabled)) + }; + } + + /// Checks to see if this RealTimeClock is running + pub fn is_running(&self) -> bool { + unsafe { self.inner.regs().ctrl().read().rtc_active() } + } + + /// Set the datetime to a new value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { + self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; + + // disable RTC while we configure it + unsafe { + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + + self.inner.regs().setup_0().write(|w| { + self::datetime::write_setup_0(&t, w); + }); + self.inner.regs().setup_1().write(|w| { + self::datetime::write_setup_1(&t, w); + }); + + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + } + Ok(()) + } + + /// Return the current datetime. + /// + /// # Errors + /// + /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. + pub fn now(&self) -> Result { + if !self.is_running() { + return Err(RtcError::NotRunning); + } + + let rtc_0 = unsafe { self.inner.regs().rtc_0().read() }; + let rtc_1 = unsafe { self.inner.regs().rtc_1().read() }; + + self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime) + } + + /// Disable the alarm that was scheduled with [`schedule_alarm`]. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn disable_alarm(&mut self) { + unsafe { + self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false)); + + while self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Schedule an alarm. The `filter` determines at which point in time this alarm is set. + /// + /// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call: + /// ```no_run + /// # #[cfg(feature = "chrono")] + /// # fn main() { } + /// # #[cfg(not(feature = "chrono"))] + /// # fn main() { + /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; + /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// let now = real_time_clock.now().unwrap(); + /// real_time_clock.schedule_alarm( + /// DateTimeFilter::default() + /// .minute(if now.minute == 59 { 0 } else { now.minute + 1 }) + /// ); + /// # } + /// ``` + pub fn schedule_alarm(&mut self, filter: DateTimeFilter) { + self.disable_alarm(); + + unsafe { + self.inner.regs().irq_setup_0().write(|w| { + filter.write_setup_0(w); + }); + self.inner.regs().irq_setup_1().write(|w| { + filter.write_setup_1(w); + }); + + // Set the enable bit and check if it is set + self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); + while !self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered, + /// or the next [`schedule_alarm`] will never fire. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn clear_interrupt(&mut self) { + self.disable_alarm(); + } +} + +/// Errors that can occur on methods on [RtcClock] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RtcError { + /// An invalid DateTime was given or stored on the hardware. + InvalidDateTime(DateTimeError), + + /// The RTC clock is not running + NotRunning, +} + +mod sealed { + pub trait Instance { + fn regs(&self) -> crate::pac::rtc::Rtc; + } +} + +pub trait Instance: sealed::Instance {} + +impl sealed::Instance for crate::peripherals::RTC { + fn regs(&self) -> crate::pac::rtc::Rtc { + crate::pac::RTC + } +} +impl Instance for crate::peripherals::RTC {} From c495c765df42ca273da55b320c869b0aaabc6ef8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:28:35 +0200 Subject: [PATCH 09/62] Enable embedded-io on nightly --- embassy-rp/Cargo.toml | 44 ++++++++++++++----------------------------- embassy-rp/src/lib.rs | 2 -- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 92780ee39..3debca710 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -7,7 +7,9 @@ edition = "2021" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] -flavors = [{ name = "rp2040", target = "thumbv6m-none-eabi" }] +flavors = [ + { name = "rp2040", target = "thumbv6m-none-eabi" }, +] [features] defmt = ["dep:defmt", "embassy-usb?/defmt"] @@ -18,16 +20,8 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] -time-driver = [] - # Enable nightly-only features -nightly = [ - "embassy-executor/nightly", - "embedded-hal-1", - "embedded-hal-async", - "embassy-embedded-hal/nightly", - "dep:embassy-usb", -] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -36,15 +30,11 @@ unstable-traits = ["embedded-hal-1"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ - "tick-hz-1_000_000", -] } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = [ - "prio-bits-2", -] } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -53,18 +43,12 @@ cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" -futures = { version = "0.3.17", default-features = false, features = [ - "async-await", -] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.3.0", features = ["async"], optional = true } -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev = "017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = [ - "rt", -] } +rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } -embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ - "unproven", -] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true } -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} +embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 8dcefece2..aebbbf567 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,7 +8,6 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; -#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -109,7 +108,6 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); - #[cfg(feature = "time-driver")] timer::init(); dma::init(); } From 1c657d2d5532979f326d89a0d40e8aa4bc96d0c2 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 16:45:59 +0200 Subject: [PATCH 10/62] Add time-driver feature to docs --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 1e26f7dd2..7315d673c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] flavors = [ { name = "rp2040", target = "thumbv6m-none-eabi" }, ] From ab1a6889a62e86a80af6fc572ffa992cfb9ef960 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:02:05 -0700 Subject: [PATCH 11/62] rp: fix async SPI read and write --- embassy-rp/src/dma.rs | 38 ++++++++++++++++++++++++++++ embassy-rp/src/spi.rs | 59 ++++++++++++++++++++++++++++++------------- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index acf338225..b256cc2f0 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,6 +56,25 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } +pub unsafe fn read_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let mut dummy: u32 = 0; + copy_inner( + ch, + from as *const u32, + &mut dummy as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], @@ -75,6 +94,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } +pub unsafe fn write_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + to: *mut W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let dummy: u32 = 0; + copy_inner( + ch, + &dummy as *const u32, + to as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn copy<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: &[W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 74f0b04de..e7cd99929 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,53 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); reg.set_txdmae(true); - }); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read_repeated( + rx_ch, + self.inner.regs().dr().ptr() as *const u8, + buffer.len(), + T::RX_DREQ, + ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { reg.set_rxdmae(true); - }); + reg.set_txdmae(true); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } @@ -364,20 +387,20 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); assert_eq!(from_len, to_len); + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + }) + }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_txdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_rxdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) From 295cc997ae8b1468617dd9f430be4e502901f4f2 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:23:17 -0700 Subject: [PATCH 12/62] rp: let SPI RX overflow during async write --- embassy-rp/src/dma.rs | 19 ------------------- embassy-rp/src/spi.rs | 35 +++++++++++++++++------------------ 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index b256cc2f0..7ad1a6bfe 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,25 +56,6 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } -pub unsafe fn read_repeated<'a, C: Channel, W: Word>( - ch: impl Peripheral

+ 'a, - from: *const W, - len: usize, - dreq: u8, -) -> Transfer<'a, C> { - let mut dummy: u32 = 0; - copy_inner( - ch, - from as *const u32, - &mut dummy as *mut u32, - len, - W::size(), - false, - false, - dreq, - ) -} - pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e7cd99929..3cf823573 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,29 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - unsafe { - self.inner.regs().dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }) - }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_txdmae(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - let rx_ch = self.rx_dma.as_mut().unwrap(); - let rx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read_repeated( - rx_ch, - self.inner.regs().dr().ptr() as *const u8, - buffer.len(), - T::RX_DREQ, - ) - }; - join(tx_transfer, rx_transfer).await; + tx_transfer.await; + + let p = self.inner.regs(); + unsafe { + while p.sr().read().bsy() {} + + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); + } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); + } + Ok(()) } From 0c6933fefb749aa8857ade8cf3d52cb38a398d55 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 14:54:24 -0700 Subject: [PATCH 13/62] rp: remove extraneous newlines in logs --- embassy-rp/src/gpio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a0328302a..9b9a08110 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() { w.set_edge_low(pin_group, false); } InterruptTrigger::LevelHigh => { - debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin); + debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); w.set_level_high(pin_group, false); } InterruptTrigger::LevelLow => { @@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { - debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin()); + debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); } InterruptTrigger::LevelLow => { @@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // the pin and if it has been disabled that means it was done by the // interrupt service routine, so we then know that the event/trigger // happened and Poll::Ready will be returned. - debug!("{:?} for pin {}\n", self.level, self.pin.pin()); + debug!("{:?} for pin {}", self.level, self.pin.pin()); match self.level { InterruptTrigger::AnyEdge => { if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelHigh => { if !inte.level_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelLow => { if !inte.level_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeHigh => { if !inte.edge_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeLow => { if !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } } - debug!("InputFuture::poll return Poll::Pending\n"); + debug!("InputFuture::poll return Poll::Pending"); Poll::Pending } } From d0fe654c82b548d65f49213ad50fc2edc5b3d71e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 09:42:40 +0200 Subject: [PATCH 14/62] Remove BootFlash borrow Compiler will infer a different lifetime for BootFlash than for the borrowed flash, which makes it require more type annotations than if it was just owning the type. Since it doesn't really matter if it owns or borrows in practical use, change it to own so that it simplifies usage. --- embassy-boot/boot/src/lib.rs | 24 +++++++++++----------- examples/boot/bootloader/nrf/src/main.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 7 +++---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4a2b112a9..015dd58db 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -447,24 +447,24 @@ where } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> +pub struct BootFlash where F: NorFlash + ReadNorFlash, { - flash: &'a mut F, + flash: F, } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl BootFlash where F: NorFlash + ReadNorFlash, { /// Create a new instance of a bootable flash - pub fn new(flash: &'a mut F) -> Self { + pub fn new(flash: F) -> Self { Self { flash } } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl Flash for BootFlash where F: NorFlash + ReadNorFlash, { @@ -472,14 +472,14 @@ where const ERASE_VALUE: u8 = ERASE_VALUE; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ErrorType for BootFlash where F: ReadNorFlash + NorFlash, { type Error = F::Error; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl NorFlash for BootFlash where F: ReadNorFlash + NorFlash, { @@ -487,26 +487,26 @@ where const ERASE_SIZE: usize = F::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(self.flash, from, to) + F::erase(&mut self.flash, from, to) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(self.flash, offset, bytes) + F::write(&mut self.flash, offset, bytes) } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ReadNorFlash for BootFlash where F: ReadNorFlash + NorFlash, { const READ_SIZE: usize = F::READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(self.flash, offset, bytes) + F::read(&mut self.flash, offset, bytes) } fn capacity(&self) -> usize { - F::capacity(self.flash) + F::capacity(&self.flash) } } diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 9031997c2..8266206b3 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -21,7 +21,7 @@ fn main() -> ! { let mut bl = BootLoader::default(); let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), + WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), ))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index bb5d3e531..294464d1c 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,10 +20,9 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let mut flash = Flash::unlock(p.FLASH); - let start = bl.prepare(&mut SingleFlashConfig::new( - &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash), - )); + let flash = Flash::unlock(p.FLASH); + let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } } From b418c0e4d620db0332d02c16fbbd455e7b8805a9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 14:03:04 +0200 Subject: [PATCH 15/62] Take into account size of revert index Fixes a bug in the partition assertions that ensures that the state page(s) have enough space for 2x active partition range. Add unit test to verify that panic is observed. --- embassy-boot/boot/src/lib.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 015dd58db..3d359533e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -222,10 +222,7 @@ impl BootLoader { page: &mut [u8], ) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(self.active.len() % page.len(), 0); - assert_eq!(self.dfu.len() % page.len(), 0); - assert!(self.dfu.len() - self.active.len() >= page.len()); - assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE); + assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); assert_eq!(magic.len(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active @@ -409,6 +406,13 @@ impl BootLoader { } } +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { + assert_eq!(active.len() % page_size, 0); + assert_eq!(dfu.len() % page_size, 0); + assert!(dfu.len() - active.len() >= page_size); + assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); +} + /// Convenience provider that uses a single flash for all partitions. pub struct SingleFlashConfig<'a, F> where @@ -919,6 +923,15 @@ mod tests { } } + #[test] + #[should_panic] + fn test_range_asserts() { + const ACTIVE: Partition = Partition::new(4096, 4194304); + const DFU: Partition = Partition::new(4194304, 2 * 4194304); + const STATE: Partition = Partition::new(0, 4096); + assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + } + struct MemFlash([u8; SIZE]); impl NorFlash From 1d3e41f970c1b04fc98533a0a6d09cb5be85ff09 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 21 Sep 2022 06:00:35 +0200 Subject: [PATCH 16/62] Remove code-duplication in async bufferedUart implementations --- embassy-rp/src/uart/buffered.rs | 215 +++++++++++++------------------- 1 file changed, 89 insertions(+), 126 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 3eb96e3d5..6d395b6f4 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,5 +1,5 @@ use core::future::Future; -use core::task::Poll; +use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; @@ -87,9 +87,9 @@ impl<'d, T: Instance> BufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); + w.set_txim(true); }); } @@ -122,7 +122,6 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); }); @@ -151,9 +150,7 @@ impl<'d, T: Instance> TxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? - w.set_rxim(true); - w.set_rtim(true); + w.set_txim(true); }); } @@ -179,6 +176,51 @@ where } } +impl<'d, T: Instance> RxStateInner<'d, T> +where + Self: 'd, +{ + fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll>, bool) { + // We have data ready in buffer? Return it. + let mut do_pend = false; + let data = self.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if self.buf.is_full() { + do_pend = true; + } + self.buf.pop(len); + + return (Poll::Ready(Ok(len)), do_pend); + } + + self.waker.register(waker); + (Poll::Pending, do_pend) + } + + fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll> { + // We have data ready in buffer? Return it. + let buf = self.buf.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + self.waker.register(waker); + Poll::Pending + } + + fn consume(&mut self, amt: usize) -> bool { + let full = self.buf.is_full(); + self.buf.pop(amt); + full + } +} + impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> where Self: 'd, @@ -240,6 +282,35 @@ where } } +impl<'d, T: Instance> TxStateInner<'d, T> +where + Self: 'd, +{ + fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll>, bool) { + let empty = self.buf.is_empty(); + let tx_buf = self.buf.push_buf(); + if tx_buf.is_empty() { + self.waker.register(waker); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + self.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + } + + fn flush(&mut self, waker: &Waker) -> Poll> { + if !self.buf.is_empty() { + self.waker.register(waker); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + } +} + impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> where Self: 'd, @@ -299,26 +370,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.buf.is_full() { - do_pend = true; - } - state.rx.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx.waker.register(cx.waker()); - Poll::Pending + state.rx.read(buf, cx.waker()) }); if do_pend { @@ -337,26 +391,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.buf.is_full() { - do_pend = true; - } - state.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.waker.register(cx.waker()); - Poll::Pending + state.read(buf, cx.waker()) }); if do_pend { @@ -377,28 +414,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.rx.waker.register(cx.waker()); - Poll::>::Pending + state.rx.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.buf.is_full(); - state.rx.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.rx.consume(amt)); if signal { self.inner.pend(); } @@ -414,28 +436,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.waker.register(cx.waker()); - Poll::>::Pending + state.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.buf.is_full(); - state.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.consume(amt)); if signal { self.inner.pend(); } @@ -449,20 +456,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.buf.is_empty(); - let tx_buf = state.tx.buf.push_buf(); - if tx_buf.is_empty() { - state.tx.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -475,16 +469,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.buf.is_empty() { - state.tx.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) } } @@ -495,20 +480,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.buf.is_empty(); - let tx_buf = state.buf.push_buf(); - if tx_buf.is_empty() { - state.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -521,15 +493,6 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.buf.is_empty() { - state.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) } } From 3d708a459c9c5f18ddd5c63a06f272371b6225c8 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 10:47:49 +0200 Subject: [PATCH 17/62] Implement proper `Drop` for `BufferedUarte` --- embassy-nrf/src/buffered_uarte.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba2470..84ef86c96 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -27,7 +27,7 @@ use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; -use crate::gpio::Pin as GpioPin; +use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::InterruptExt; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; @@ -427,23 +427,26 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { + debug!("oh no, dropping uarte"); let r = U::regs(); // TODO this probably deadlocks. do like Uarte instead. + r.inten.reset(); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); - self.timer.stop(); - if let RxState::Receiving = self.rx_state { - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - } - if let TxState::Transmitting(_) = self.tx_state { - r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - } - if let RxState::Receiving = self.rx_state { - low_power_wait_until(|| r.events_endrx.read().bits() == 1); - } - if let TxState::Transmitting(_) = self.tx_state { - low_power_wait_until(|| r.events_endtx.read().bits() == 1); - } + r.events_txstopped.reset(); + r.tasks_stoptx.write(|w| w.tasks_stoptx().set_bit()); + while !r.events_txstopped.read().events_txstopped().bit_is_set() {} + + while !r.events_rxto.read().events_rxto().bit_is_set() {} + + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.rxd.read().bits()); + gpio::deconfigure_pin(r.psel.txd.read().bits()); + gpio::deconfigure_pin(r.psel.rts.read().bits()); + gpio::deconfigure_pin(r.psel.cts.read().bits()); } } From 0f55f5a73d356dc991dbc3c4bc102e7d652c5fc5 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 11:06:06 +0200 Subject: [PATCH 18/62] Remove left-in comments and logs --- embassy-nrf/src/buffered_uarte.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 84ef86c96..3ee3e9477 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -427,10 +427,8 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { - debug!("oh no, dropping uarte"); let r = U::regs(); - // TODO this probably deadlocks. do like Uarte instead. r.inten.reset(); r.events_rxto.reset(); r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); From 15b4f9db9000cfb402357c5ac2c84876a3ad27c3 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 11:19:47 +0200 Subject: [PATCH 19/62] Remove unused function --- embassy-nrf/src/buffered_uarte.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 3ee3e9477..e212e9897 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -550,13 +550,3 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, trace!("irq: end"); } } - -/// Low power blocking wait loop using WFE/SEV. -fn low_power_wait_until(mut condition: impl FnMut() -> bool) { - while !condition() { - // WFE might "eat" an event that would have otherwise woken the executor. - cortex_m::asm::wfe(); - } - // Retrigger an event to be transparent to the executor. - cortex_m::asm::sev(); -} From 5f7e0eb2aea6f7f6e23d9a5b7400b29377e57d8e Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 14:06:56 +0200 Subject: [PATCH 20/62] Fix builds on other nrf pacs --- embassy-nrf/src/buffered_uarte.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index e212e9897..eb0b1b0cd 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -431,13 +431,13 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { r.inten.reset(); r.events_rxto.reset(); - r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); r.events_txstopped.reset(); - r.tasks_stoptx.write(|w| w.tasks_stoptx().set_bit()); - while !r.events_txstopped.read().events_txstopped().bit_is_set() {} + r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + while r.events_txstopped.read().bits() == 0 {} - while !r.events_rxto.read().events_rxto().bit_is_set() {} + while r.events_rxto.read().bits() == 0 {} r.enable.write(|w| w.enable().disabled()); From 5914d80968a6aca99f0018148e4b4ed7c4e06bf0 Mon Sep 17 00:00:00 2001 From: Andrew Ealovega Date: Wed, 21 Sep 2022 22:29:57 -0400 Subject: [PATCH 21/62] Add non blocking Bxcan constructor. Signed-off-by: Andrew Ealovega --- embassy-stm32/src/can/bxcan.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index c0bd44e0f..bd92b35a0 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -12,6 +12,7 @@ pub struct Can<'d, T: Instance> { } impl<'d, T: Instance> Can<'d, T> { + /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. pub fn new( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -31,6 +32,28 @@ impl<'d, T: Instance> Can<'d, T> { can: bxcan::Can::builder(BxcanInstance(peri)).enable(), } } + + /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. + /// You must call [Can::enable_non_blocking] to use the peripheral. + pub fn new_disabled( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, rx, tx); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + + T::enable(); + T::reset(); + + Self { + can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), + } + } } impl<'d, T: Instance> Drop for Can<'d, T> { From 897b72c872183221e088611aa6f30989800afd2b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:28:56 +0200 Subject: [PATCH 22/62] Update Rust nightly. Removes feature(generic_associated_types) --- embassy-boot/boot/src/lib.rs | 1 - embassy-boot/nrf/src/lib.rs | 1 - embassy-boot/stm32/src/lib.rs | 1 - embassy-embedded-hal/src/lib.rs | 2 +- embassy-lora/src/lib.rs | 1 - embassy-net/src/lib.rs | 2 +- embassy-nrf/src/lib.rs | 2 +- embassy-rp/src/lib.rs | 2 +- embassy-stm32/src/lib.rs | 2 +- embassy-sync/src/lib.rs | 2 +- embassy-time/src/lib.rs | 2 +- embassy-usb-hid/src/lib.rs | 1 - embassy-usb-serial/src/lib.rs | 1 - embassy-usb/src/lib.rs | 1 - examples/boot/application/nrf/src/bin/a.rs | 1 - examples/boot/application/nrf/src/bin/b.rs | 1 - examples/nrf/src/bin/usb_ethernet.rs | 1 - examples/nrf/src/bin/usb_hid_keyboard.rs | 1 - examples/nrf/src/bin/usb_hid_mouse.rs | 1 - examples/nrf/src/bin/usb_serial.rs | 1 - examples/nrf/src/bin/usb_serial_multitask.rs | 1 - examples/rp/src/bin/usb_ethernet.rs | 1 - examples/rp/src/bin/usb_serial.rs | 1 - examples/stm32l0/src/bin/lorawan.rs | 1 - examples/stm32l5/src/bin/usb_ethernet.rs | 1 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 1 - examples/stm32wl/src/bin/lorawan.rs | 1 - examples/stm32wl/src/bin/subghz.rs | 1 - rust-toolchain.toml | 2 +- 29 files changed, 8 insertions(+), 29 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 3d359533e..96878ace9 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] #![no_std] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 0c14781a2..385e089fe 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 39f080517..edba39cca 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 0c6f2786a..a12a3a3a0 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 2483dcb2e..90ba0d1d4 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device //! crate's async LoRaWAN MAC implementation. diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 8eebc798e..4d30550d3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f3b3ca0ca..d7bd21702 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -43,7 +43,7 @@ //! mutable slices always reside in RAM. #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c3976d261..9ce09064a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 30ff02d56..0392e8086 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 25150e8aa..80bb907a3 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 5b2620986..4edc883fe 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index 5fee60bbc..8b181aec8 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] //! Implements HID functionality for a usb-device device. diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index f3de2ec1b..27b536a6b 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index b165804ef..ca7dde627 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 133a3e678..7a404a914 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_boot_nrf::FirmwareUpdater; diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 5394bf0c7..1373f277d 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 352660b59..33ca380ff 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 7fdb0b685..4761fcf66 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 7cd2ece17..f1b57a17f 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a68edb329..f9526cbc4 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index d62d7e520..c646c0bbd 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 2df7f62f6..166ffe175 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 74be1f598..00cf3e93f 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::{info, panic}; diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 00ff67f3f..27d7c29c2 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -3,7 +3,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 2c8706e41..c96a83ead 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 7d763e7fd..0aca6f1cd 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 9143e64da..7f34dd306 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 8f674d796..3c60a8de4 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f5e342edc..1ec19e58b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2022-08-16" +channel = "nightly-2022-09-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", From a0487380da42a71ab7532e2bc1befd1039c18a78 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:42:49 +0200 Subject: [PATCH 23/62] Replace futures::future::poll_fn -> core::future::poll_fn. --- embassy-executor/src/spawner.rs | 3 +-- embassy-lora/src/stm32wl/mod.rs | 3 +-- embassy-net/src/stack.rs | 3 +-- embassy-net/src/tcp.rs | 6 +++--- embassy-net/src/udp.rs | 2 +- embassy-nrf/src/buffered_uarte.rs | 3 +-- embassy-nrf/src/gpiote.rs | 3 +-- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 2 +- embassy-nrf/src/rng.rs | 2 +- embassy-nrf/src/saadc.rs | 2 +- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/temp.rs | 2 +- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/twim.rs | 3 +-- embassy-nrf/src/uarte.rs | 2 +- embassy-nrf/src/usb.rs | 3 +-- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/usb.rs | 3 +-- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 3 +-- embassy-stm32/src/usb/usb.rs | 3 +-- embassy-sync/src/mutex.rs | 3 +-- embassy-sync/src/signal.rs | 4 ++-- examples/nrf-rtos-trace/src/bin/rtos_trace.rs | 3 ++- examples/nrf/src/bin/executor_fairness_test.rs | 3 ++- 29 files changed, 34 insertions(+), 43 deletions(-) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 25a0d7dbb..400d973ff 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,10 +1,9 @@ +use core::future::poll_fn; use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; use core::task::Poll; -use futures_util::future::poll_fn; - use super::raw; /// Token to spawn a newly-created task in an executor. diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 4d11244b6..e28fa2c1a 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,5 +1,5 @@ //! A radio driver integration for the radio found on STM32WL family devices. -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -11,7 +11,6 @@ use embassy_stm32::subghz::{ Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 8d2dd4bca..3a7610758 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,10 +1,9 @@ use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; -use futures::future::poll_fn; use futures::pin_mut; use heapless::Vec; #[cfg(feature = "dhcpv4")] diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0fa873602..f8fff3e2d 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; @@ -103,7 +103,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)), tcp::State::Listen => unreachable!(), @@ -128,7 +128,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { s.register_send_waker(cx.waker()); diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 78b09a492..f2e33493c 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba2470..fec875cb8 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,7 +15,7 @@ use core::cell::RefCell; use core::cmp::min; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -23,7 +23,6 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag use embassy_hal_common::ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index d99f592b0..b418be9d5 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,10 +1,9 @@ use core::convert::Infallible; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 762e09715..253c85c32 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,10 +1,10 @@ //! Quadrature decoder interface +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index c97cb1656..ea0a17031 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,11 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::ptr; use core::task::Poll; use embassy_hal_common::drop::DropBomb; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 42da51d0f..e0caeaaee 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,3 +1,4 @@ +use core::future::poll_fn; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; @@ -5,7 +6,6 @@ use core::task::Poll; 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::interrupt::InterruptExt; use crate::peripherals::RNG; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 9bc89eb38..d1c82423e 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; // We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 2955182e4..51cd73a47 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use futures::future::poll_fn; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index d520fd686..7a7f61b51 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -1,12 +1,12 @@ //! Temperature sensor interface. +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3de5a8962..bc8710640 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -1,12 +1,12 @@ #![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 futures::future::poll_fn; use crate::interrupt::{Interrupt, InterruptExt}; use crate::ppi::{Event, Task}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 3d4af753a..7c6ca1d30 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -6,7 +6,7 @@ //! //! - nRF52832: Section 33 //! - nRF52840: Section 6.31 -use core::future::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use futures::future::poll_fn; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index c250e24ca..5f9c4f17d 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -13,12 +13,12 @@ //! memory may be used given that buffers are passed in directly to its read and write //! methods. +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 688326e9c..0685d419c 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; @@ -11,8 +12,6 @@ use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 7ad1a6bfe..410c48666 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,3 +1,4 @@ +use core::future::Future; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; @@ -5,7 +6,6 @@ use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::Future; use pac::dma::vals::DataSize; use crate::pac::dma::vals; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 82eafdefd..a7ec5fb79 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,3 +1,4 @@ +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::slice; use core::sync::atomic::Ordering; @@ -8,8 +9,6 @@ use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index fb9dc9d08..ff9157d51 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -1,8 +1,8 @@ +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::AFType; use crate::gpio::Speed; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index db4924461..b7c89931c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,4 +1,5 @@ use core::cmp; +use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; @@ -6,7 +7,6 @@ use embassy_embedded_hal::SetConfig; 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::dma::NoDma; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 520f2ab9a..10fc4a75e 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,10 +1,10 @@ #![macro_use] +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use rand_core::{CryptoRng, RngCore}; use crate::{pac, peripherals, Peripheral}; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 67758c492..a8bc6385f 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1,12 +1,12 @@ #![macro_use] use core::default::Default; +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; use crate::dma::NoDma; diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 5f6dabb3b..46c49a997 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; use super::*; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index db965824a..e5ee1181c 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; @@ -10,8 +11,6 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 92101c6b5..fcf056d36 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -2,11 +2,10 @@ //! //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; +use core::future::poll_fn; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use futures_util::future::poll_fn; - use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex as BlockingMutex; use crate::waitqueue::WakerRegistration; diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 34201d03a..b4d99513a 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,6 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; @@ -94,7 +94,7 @@ impl Signal { /// Future that completes when this Signal has been signaled. pub fn wait(&self) -> impl Future + '_ { - futures_util::future::poll_fn(move |cx| self.poll_wait(cx)) + poll_fn(move |cx| self.poll_wait(cx)) } /// non-blocking method to check whether this signal has been signaled. diff --git a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs index 7d1ad87c8..cf8b2f808 100644 --- a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs +++ b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use embassy_executor::Spawner; @@ -46,7 +47,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf/src/bin/executor_fairness_test.rs index 9ae030d07..2a28f2763 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf/src/bin/executor_fairness_test.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use defmt::{info, unwrap}; @@ -26,7 +27,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) From 10d1ad2343825388277dc54db649c9349a0b6de8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:48:35 +0200 Subject: [PATCH 24/62] Replace futures::future::join -> embassy_futures::join::join. --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/spi.rs | 2 +- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/dcmi.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- examples/nrf/src/bin/usb_hid_mouse.rs | 2 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 + examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 1 + examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 1 + examples/stm32l5/src/bin/usb_hid_mouse.rs | 2 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/gpio_async.rs | 2 +- 19 files changed, 20 insertions(+), 13 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 63997c99f..885a4746d 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -33,6 +33,7 @@ unstable-traits = ["embedded-hal-1"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 3cf823573..03293e064 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -1,9 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; -use futures::future::join; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin as _; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b4c19f32e..484496f24 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,6 +34,7 @@ flavors = [ embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index ff9157d51..20e1a4070 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -429,7 +429,7 @@ where } }); - let (_, result) = futures::future::join(dma_read, result).await; + let (_, result) = embassy_futures::join::join(dma_read, result).await; unsafe { Self::toggle(false) }; @@ -537,7 +537,7 @@ where unsafe { Self::toggle(true) }; - let (_, result) = futures::future::join(dma_result, result).await; + let (_, result) = embassy_futures::join::join(dma_result, result).await; unsafe { Self::toggle(false) }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 02e6020b0..556d12305 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -3,9 +3,9 @@ use core::ptr; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use futures::future::join; use self::sealed::WordSize; use crate::dma::{slice_ptr_parts, NoDma, Transfer}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4761fcf66..70318b78f 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -7,6 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; 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, PowerUsb}; @@ -15,7 +16,6 @@ use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index f1b57a17f..65fbda1cf 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -6,13 +6,13 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index f9526cbc4..a740b4e0a 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,12 +6,12 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 17393322c..24c3cdd67 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,6 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 00cf3e93f..bf92a1636 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -4,12 +4,12 @@ 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_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 33ac63db1..895e043dd 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -10,6 +10,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a9c46068f..a14e728ba 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 208f39080..27f5c260a 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -11,6 +11,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index d3702fc35..b9fd20e2b 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index c451bd221..05945f6bf 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -15,6 +15,7 @@ embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", fea embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" defmt = "0.3" diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 0aca6f1cd..fa92ceae3 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; @@ -11,7 +12,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index b576a7353..7484dc832 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -4,13 +4,13 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 4d6877ccd..11ecb9169 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,6 +8,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 1eeaac1f6..f20b8fcbd 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -4,9 +4,9 @@ use defmt::{assert, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_time::{Duration, Instant, Timer}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 2e7916c5feed13893921936ce26dab44c8a617a9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 06:38:47 +0200 Subject: [PATCH 25/62] Add RP2040 ROM functions and intrinsics aliases --- embassy-rp/Cargo.toml | 4 + embassy-rp/src/intrinsics.rs | 273 +++++++++++++ embassy-rp/src/lib.rs | 3 + embassy-rp/src/rom_data.rs | 730 +++++++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 embassy-rp/src/intrinsics.rs create mode 100644 embassy-rp/src/rom_data.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 885a4746d..df0af8dfb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -22,6 +22,10 @@ unstable-pac = [] time-driver = [] +rom-func-cache = [] +disable-intrinsics = [] +rom-v2-intrinsics = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs new file mode 100644 index 000000000..9e6624cf0 --- /dev/null +++ b/embassy-rp/src/intrinsics.rs @@ -0,0 +1,273 @@ +#![macro_use] + +/// Generate a series of aliases for an intrinsic function. +macro_rules! intrinsics_aliases { + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; + + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; +} + +/// The macro used to define overridden intrinsics. +/// +/// This is heavily inspired by the macro used by compiler-builtins. The idea +/// is to abstract anything special that needs to be done to override an +/// intrinsic function. Intrinsic generation is disabled for non-ARM targets +/// so things like CI and docs generation do not have problems. Additionally +/// they can be disabled with the crate feature `disable-intrinsics` for +/// testing or comparing performance. +/// +/// Like the compiler-builtins macro, it accepts a series of functions that +/// looks like normal Rust code: +/// +/// intrinsics! { +/// extern "C" fn foo(a: i32) -> u32 { +/// // ... +/// } +/// +/// #[nonstandard_attribute] +/// extern "C" fn bar(a: i32) -> u32 { +/// // ... +/// } +/// } +/// +/// Each function can also be decorated with nonstandard attributes to control +/// additional behaviour: +/// +/// * `slower_than_default` - indicates that the override is slower than the +/// default implementation. Currently this just disables the override +/// entirely. +/// * `bootrom_v2` - indicates that the override is only available +/// on a V2 bootrom or higher. Only enabled when the feature +/// `rom-v2-intrinsics` is set. +/// * `alias` - accepts a list of names to alias the intrinsic to. +/// * `aeabi` - accepts a list of ARM EABI names to alias to. +/// +macro_rules! intrinsics { + () => {}; + + ( + #[slower_than_default] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + #[bootrom_v2] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(feature = "rom-v2-intrinsics"))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(feature = "rom-v2-intrinsics")] + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[aeabi = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ce09064a..9ac98d226 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -4,9 +4,12 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod intrinsics; + pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rom_data; pub mod rtc; pub mod spi; #[cfg(feature = "time-driver")] diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs new file mode 100644 index 000000000..93a3632a5 --- /dev/null +++ b/embassy-rp/src/rom_data.rs @@ -0,0 +1,730 @@ +//! Functions and data from the RPI Bootrom. +//! +//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: +//! +//! > The Bootrom contains a number of public functions that provide useful +//! > RP2040 functionality that might be needed in the absence of any other code +//! > on the device, as well as highly optimized versions of certain key +//! > functionality that would otherwise have to take up space in most user +//! > binaries. + +/// A bootrom function table code. +pub type RomFnTableCode = [u8; 2]; + +/// This function searches for (table) +type RomTableLookupFn = unsafe extern "C" fn(*const u16, u32) -> T; + +/// The following addresses are described at `2.8.2. Bootrom Contents` +/// Pointer to the lookup table function supplied by the rom. +const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _; + +/// Pointer to helper functions lookup table. +const FUNC_TABLE: *const u16 = 0x0000_0014 as _; + +/// Pointer to the public data lookup table. +const DATA_TABLE: *const u16 = 0x0000_0016 as _; + +/// Address of the version number of the ROM. +const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; + +/// Retrive rom content from a table using a code. +fn rom_table_lookup(table: *const u16, tag: RomFnTableCode) -> T { + unsafe { + let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR); + let rom_table_lookup: RomTableLookupFn = core::mem::transmute(rom_table_lookup_ptr); + rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32) + } +} + +/// To save space, the ROM likes to store memory pointers (which are 32-bit on +/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the +/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped +/// to address `0x0000_0000` and so 16-bits are always sufficient. +/// +/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer. +unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 { + let ptr: u16 = *rom_address; + ptr as *const u32 +} + +macro_rules! declare_rom_function { + ( + $(#[$outer:meta])* + fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; + + ( + $(#[$outer:meta])* + unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; +} + +macro_rules! rom_functions { + () => {}; + + ( + $(#[$outer:meta])* + $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; + + ( + $(#[$outer:meta])* + $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; +} + +rom_functions! { + /// Return a count of the number of 1 bits in value. + b"P3" fn popcount32(value: u32) -> u32; + + /// Return the bits of value in the reverse order. + b"R3" fn reverse32(value: u32) -> u32; + + /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32. + b"L3" fn clz32(value: u32) -> u32; + + /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32. + b"T3" fn ctz32(value: u32) -> u32; + + /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: + /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED + /// for the USB Mass Storage Device: + /// * 0 No pins are used as per cold boot. + /// * Otherwise a single bit set indicating which GPIO pin should be set to output and + /// raised whenever there is mass storage activity from the host. + /// * disable_interface_mask may be used to control the exposed USB interfaces: + /// * 0 To enable both interfaces (as per cold boot). + /// * 1 To disable the USB Mass Storage Interface. + /// * 2 to Disable the USB PICOBOOT Interface. + b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); + + /// Sets n bytes start at ptr to the value c and returns ptr + b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8; + + /// Sets n bytes start at ptr to the value c and returns ptr. + /// + /// Note this is a slightly more efficient variant of _memset that may only + /// be used if ptr is word aligned. + // Note the datasheet does not match the actual ROM for the code here, see + // https://github.com/raspberrypi/pico-feedback/issues/217 + b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + /// + /// Note this is a slightly more efficient variant of _memcpy that may only be + /// used if dest and src are word aligned. + b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8; + + /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads. + b"IF" unsafe fn connect_internal_flash() -> (); + + /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence. + /// + /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be + /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This + /// function configures the SSI with a fixed SCK clock divisor of /6. + b"EX" unsafe fn flash_exit_xip() -> (); + + /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a + /// block erase command e.g. D8h block erase, and the size of the block erased by this + /// command — this function will use the larger block erase where possible, for much higher + /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of + /// 4096 bytes. + b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> (); + + /// Program data to a range of flash addresses starting at `addr` (and + /// offset from the start of flash) and `count` bytes in size. The value + /// `addr` must be aligned to a 256-byte boundary, and `count` must be a + /// multiple of 256. + b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> (); + + /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can + /// drive the flashchip select as normal. + b"FC" unsafe fn flash_flush_cache() -> (); + + /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits, + /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported. + /// The debugger calls this function after performing a flash erase/programming operation, so + /// that the freshly-programmed code and data is visible to the debug host, without having to + /// know exactly what kind of flash device is connected. + b"CX" unsafe fn flash_enter_cmd_xip() -> (); + + /// This is the method that is entered by core 1 on reset to wait to be launched by core 0. + /// There are few cases where you should call this method (resetting core 1 is much better). + /// This method does not return and should only ever be called on core 1. + b"WV" unsafe fn wait_for_vector() -> !; +} + +// Various C intrinsics in the ROM +intrinsics! { + #[alias = __popcountdi2] + extern "C" fn __popcountsi2(x: u32) -> u32 { + popcount32(x) + } + + #[alias = __clzdi2] + extern "C" fn __clzsi2(x: u32) -> u32 { + clz32(x) + } + + #[alias = __ctzdi2] + extern "C" fn __ctzsi2(x: u32) -> u32 { + ctz32(x) + } + + // __rbit is only unofficial, but it show up in the ARM documentation, + // so may as well hook it up. + #[alias = __rbitl] + extern "C" fn __rbit(x: u32) -> u32 { + reverse32(x) + } + + unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset(dest, c as u8, n as u32); + } + + #[alias = __aeabi_memset8] + unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset4(dest as *mut u32, c as u8, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () { + memset(dest, 0, n as u32); + } + + #[alias = __aeabi_memclr8] + unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () { + memset4(dest as *mut u32, 0, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy(dest, src, n as u32); + } + + #[alias = __aeabi_memcpy8] + unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy44(dest as *mut u32, src as *const u32, n as u32); + } +} + +unsafe fn convert_str(s: *const u8) -> &'static str { + let mut end = s; + while *end != 0 { + end = end.add(1); + } + let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize); + core::str::from_utf8_unchecked(s) +} + +/// The version number of the rom. +pub fn rom_version_number() -> u8 { + unsafe { *VERSION_NUMBER } +} + +/// The Raspberry Pi Trading Ltd copyright string. +pub fn copyright_string() -> &'static str { + let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR"); + unsafe { convert_str(s) } +} + +/// The 8 most significant hex digits of the Bootrom git revision. +pub fn git_revision() -> u32 { + let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR"); + unsafe { *s } +} + +/// The start address of the floating point library code and data. +/// +/// This and fplib_end along with the individual function pointers in +/// soft_float_table can be used to copy the floating point implementation into +/// RAM if desired. +pub fn fplib_start() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FS") +} + +/// See Table 180 in the RP2040 datasheet for the contents of this table. +pub fn soft_float_table() -> *const usize { + rom_table_lookup(DATA_TABLE, *b"SF") +} + +/// The end address of the floating point library code and data. +pub fn fplib_end() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FE") +} + +/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. +pub fn soft_double_table() -> *const usize { + if rom_version_number() < 2 { + panic!( + "Double precision operations require V2 bootrom (found: V{})", + rom_version_number() + ); + } + rom_table_lookup(DATA_TABLE, *b"SD") +} + +/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms) +pub mod float_funcs { + + macro_rules! make_functions { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_functions! { + /// Calculates `a + b` + 0x00 fadd(a: f32, b: f32) -> f32; + /// Calculates `a - b` + 0x04 fsub(a: f32, b: f32) -> f32; + /// Calculates `a * b` + 0x08 fmul(a: f32, b: f32) -> f32; + /// Calculates `a / b` + 0x0c fdiv(a: f32, b: f32) -> f32; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 fsqrt(v: f32) -> f32; + /// Converts an f32 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c float_to_int(v: f32) -> i32; + /// Converts an f32 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 float_to_fix(v: f32, n: i32) -> i32; + /// Converts an f32 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 float_to_uint(v: f32) -> u32; + /// Converts an f32 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 float_to_ufix(v: f32, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// f32 value, rounding to even on tie + 0x2c int_to_float(v: i32) -> f32; + /// Converts a signed fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_float(v: i32, n: i32) -> f32; + /// Converts an unsigned integer to the nearest + /// f32 value, rounding to even on tie + 0x34 uint_to_float(v: u32) -> f32; + /// Converts an unsigned fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x38 ufix_to_float(v: u32, n: i32) -> f32; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c fcos(angle: f32) -> f32; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 fsin(angle: f32) -> f32; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 ftan(angle: f32) -> f32; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c fexp(v: f32) -> f32; + /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity + 0x50 fln(v: f32) -> f32; + } + + macro_rules! make_functions_v2 { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + if $crate::rom_data::rom_version_number() < 2 { + panic!( + "Floating point function requires V2 bootrom (found: V{})", + $crate::rom_data::rom_version_number() + ); + } + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + // These are only on BootROM v2 or higher + make_functions_v2! { + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 fcmp(a: f32, b: f32) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 fatan2(y: f32, x: f32) -> f32; + /// Converts a signed 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x5c int64_to_float(v: i64) -> f32; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_float(v: i64, n: i32) -> f32; + /// Converts an unsigned 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x64 uint64_to_float(v: u64) -> f32; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest f32 value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_float(v: u64, n: i32) -> f32; + /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c float_to_int64(v: f32) -> i64; + /// Converts an f32 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 float_to_fix64(v: f32, n: i32) -> f32; + /// Converts an f32 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 float_to_uint64(v: f32) -> u64; + /// Converts an f32 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 float_to_ufix64(v: f32, n: i32) -> u64; + /// Converts an f32 to an f64. + 0x7c float_to_double(v: f32) -> f64; + } +} + +/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms) +pub mod double_funcs { + + macro_rules! make_double_funcs { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_double_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_double_funcs! { + /// Calculates `a + b` + 0x00 dadd(a: f64, b: f64) -> f64; + /// Calculates `a - b` + 0x04 dsub(a: f64, b: f64) -> f64; + /// Calculates `a * b` + 0x08 dmul(a: f64, b: f64) -> f64; + /// Calculates `a / b` + 0x0c ddiv(a: f64, b: f64) -> f64; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 dsqrt(v: f64) -> f64; + /// Converts an f64 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c double_to_int(v: f64) -> i32; + /// Converts an f64 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 double_to_fix(v: f64, n: i32) -> i32; + /// Converts an f64 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 double_to_uint(v: f64) -> u32; + /// Converts an f64 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 double_to_ufix(v: f64, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// double value, rounding to even on tie + 0x2c int_to_double(v: i32) -> f64; + /// Converts a signed fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_double(v: i32, n: i32) -> f64; + /// Converts an unsigned integer to the nearest + /// double value, rounding to even on tie + 0x34 uint_to_double(v: u32) -> f64; + /// Converts an unsigned fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so f = + /// nearest(v/(2^n)) + 0x38 ufix_to_double(v: u32, n: i32) -> f64; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c dcos(angle: f64) -> f64; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 dsin(angle: f64) -> f64; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 dtan(angle: f64) -> f64; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c dexp(v: f64) -> f64; + /// Calculates the natural logarithm of v. If v <= 0 return -Infinity + 0x50 dln(v: f64) -> f64; + + // These are only on BootROM v2 or higher + + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 dcmp(a: f64, b: f64) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 datan2(y: f64, x: f64) -> f64; + /// Converts a signed 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x5c int64_to_double(v: i64) -> f64; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_doubl(v: i64, n: i32) -> f64; + /// Converts an unsigned 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x64 uint64_to_double(v: u64) -> f64; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest double value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_double(v: u64, n: i32) -> f64; + /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c double_to_int64(v: f64) -> i64; + /// Converts an f64 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 double_to_fix64(v: f64, n: i32) -> i64; + /// Converts an f64 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 double_to_uint64(v: f64) -> u64; + /// Converts an f64 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 double_to_ufix64(v: f64, n: i32) -> u64; + /// Converts an f64 to an f32 + 0x7c double_to_float(v: f64) -> f32; + } +} From b4f2c2a05ebe736de1faaf5541f1913ac3c4eff6 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Fri, 23 Sep 2022 12:34:02 +0200 Subject: [PATCH 26/62] Re-add timer.stop() --- embassy-nrf/src/buffered_uarte.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index eb0b1b0cd..47f32fac8 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -429,14 +429,15 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { let r = U::regs(); + self.timer.stop(); + r.inten.reset(); r.events_rxto.reset(); r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - r.events_txstopped.reset(); r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - while r.events_txstopped.read().bits() == 0 {} + while r.events_txstopped.read().bits() == 0 {} while r.events_rxto.read().bits() == 0 {} r.enable.write(|w| w.enable().disabled()); From a45fb2d7183738bdcc020a9b1916fdfab469ec0c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 24 Sep 2022 09:42:06 -0700 Subject: [PATCH 27/62] usb: fix compile errors with the log feature --- embassy-usb-serial/src/lib.rs | 6 +++--- embassy-usb/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index 27b536a6b..15c2bb0a7 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -268,7 +268,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { } /// Number of stop bits for LineCoding -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum StopBits { /// 1 stop bit @@ -292,7 +292,7 @@ impl From for StopBits { } /// Parity for LineCoding -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParityType { None = 0, @@ -316,7 +316,7 @@ impl From for ParityType { /// /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can /// be ignored if you don't plan to interface with a physical UART. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct LineCoding { stop_bits: StopBits, diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index ca7dde627..6f58c953c 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -247,7 +247,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { async fn handle_control(&mut self, req: [u8; 8]) { let req = Request::parse(&req); - trace!("control request: {:02x}", req); + trace!("control request: {:?}", req); match req.direction { UsbDirection::In => self.handle_control_in(req).await, From ca92302d038e3a8f1446085040228bac7a1d00e6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Thu, 22 Sep 2022 09:05:40 +0300 Subject: [PATCH 28/62] Parameterize Signal with RawMutex --- embassy-sync/src/signal.rs | 69 ++++++++++++++---------- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index b4d99513a..7c38637c3 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,9 +1,12 @@ //! A synchronization primitive for passing the latest value to a task. -use core::cell::UnsafeCell; +use core::cell::Cell; use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; +use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use crate::blocking_mutex::Mutex; + /// Single-slot signaling primitive. /// /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except @@ -28,8 +31,11 @@ use core::task::{Context, Poll, Waker}; /// /// static SOME_SIGNAL: Signal = Signal::new(); /// ``` -pub struct Signal { - state: UnsafeCell>, +pub struct Signal +where + R: RawMutex, +{ + state: Mutex>>, } enum State { @@ -38,24 +44,27 @@ enum State { Signaled(T), } -unsafe impl Send for Signal {} -unsafe impl Sync for Signal {} - -impl Signal { +impl Signal +where + R: RawMutex, +{ /// Create a new `Signal`. pub const fn new() -> Self { Self { - state: UnsafeCell::new(State::None), + state: Mutex::new(Cell::new(State::None)), } } } -impl Signal { +impl Signal +where + R: RawMutex, +{ /// Mark this Signal as signaled. pub fn signal(&self, val: T) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) { + self.state.lock(|cell| { + let state = cell.replace(State::Signaled(val)); + if let State::Waiting(waker) = state { waker.wake(); } }) @@ -63,31 +72,27 @@ impl Signal { /// Remove the queued value in this `Signal`, if any. pub fn reset(&self) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - *state = State::None - }) + self.state.lock(|cell| cell.set(State::None)); } - /// Manually poll the Signal future. - pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); + fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { + self.state.lock(|cell| { + let state = cell.replace(State::None); match state { State::None => { - *state = State::Waiting(cx.waker().clone()); + cell.set(State::Waiting(cx.waker().clone())); + Poll::Pending + } + State::Waiting(w) if w.will_wake(cx.waker()) => { + cell.set(State::Waiting(w)); Poll::Pending } - State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, State::Waiting(w) => { - let w = mem::replace(w, cx.waker().clone()); + cell.set(State::Waiting(cx.waker().clone())); w.wake(); Poll::Pending } - State::Signaled(_) => match mem::replace(state, State::None) { - State::Signaled(res) => Poll::Ready(res), - _ => unreachable!(), - }, + State::Signaled(res) => Poll::Ready(res), } }) } @@ -99,6 +104,14 @@ impl Signal { /// non-blocking method to check whether this signal has been signaled. pub fn signaled(&self) -> bool { - critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) + self.state.lock(|cell| { + let state = cell.replace(State::None); + + let res = matches!(state, State::Signaled(_)); + + cell.set(state); + + res + }) } } diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 70318b78f..980bc1038 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) { // Build the builder. let mut usb = builder.build(); - let remote_wakeup = Signal::new(); + let remote_wakeup: Signal<_> = Signal::new(); // Run the USB device. let usb_fut = async { From 85366661489c09fa8dec1375e9b9beee522e5e9f Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Sep 2022 12:08:46 +0300 Subject: [PATCH 29/62] Remove default, reorder generic params --- embassy-sync/src/signal.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 7c38637c3..8cb832a2b 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,7 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::Cell; use core::future::{poll_fn, Future}; -use core::mem; use core::task::{Context, Poll, Waker}; use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; From c5ce02b30e488aade19f9f859425aa127d085b92 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Sep 2022 12:08:46 +0300 Subject: [PATCH 30/62] Remove default, reorder generic params --- embassy-sync/src/signal.rs | 19 ++++++++++--------- examples/nrf/src/bin/usb_hid_keyboard.rs | 3 ++- examples/stm32h7/src/bin/signal.rs | 3 ++- examples/stm32wl/src/bin/subghz.rs | 3 ++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 8cb832a2b..c3c10a8af 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -3,7 +3,7 @@ use core::cell::Cell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll, Waker}; -use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; /// Single-slot signaling primitive. @@ -22,19 +22,20 @@ use crate::blocking_mutex::Mutex; /// /// ``` /// use embassy_sync::signal::Signal; +/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; /// /// enum SomeCommand { /// On, /// Off, /// } /// -/// static SOME_SIGNAL: Signal = Signal::new(); +/// static SOME_SIGNAL: Signal = Signal::new(); /// ``` -pub struct Signal +pub struct Signal where - R: RawMutex, + M: RawMutex, { - state: Mutex>>, + state: Mutex>>, } enum State { @@ -43,9 +44,9 @@ enum State { Signaled(T), } -impl Signal +impl Signal where - R: RawMutex, + M: RawMutex, { /// Create a new `Signal`. pub const fn new() -> Self { @@ -55,9 +56,9 @@ where } } -impl Signal +impl Signal where - R: RawMutex, + M: RawMutex, { /// Mark this Signal as signaled. pub fn signal(&self, val: T) { diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 980bc1038..4eb7d37c9 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -12,6 +12,7 @@ use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; @@ -77,7 +78,7 @@ async fn main(_spawner: Spawner) { // Build the builder. let mut usb = builder.build(); - let remote_wakeup: Signal<_> = Signal::new(); + let remote_wakeup: Signal = Signal::new(); // Run the USB device. let usb_fut = async { diff --git a/examples/stm32h7/src/bin/signal.rs b/examples/stm32h7/src/bin/signal.rs index cc3e4e3ca..6d7c168d5 100644 --- a/examples/stm32h7/src/bin/signal.rs +++ b/examples/stm32h7/src/bin/signal.rs @@ -4,11 +4,12 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -static SIGNAL: Signal = Signal::new(); +static SIGNAL: Signal = Signal::new(); #[embassy_executor::task] async fn my_sending_task() { diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 3c60a8de4..32c8b5515 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -12,6 +12,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::interrupt; use embassy_stm32::interrupt::{Interrupt, InterruptExt}; use embassy_stm32::subghz::*; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use {defmt_rtt as _, panic_probe as _}; @@ -64,7 +65,7 @@ async fn main(_spawner: Spawner) { let button = Input::new(p.PA0, Pull::Up); let mut pin = ExtiInput::new(button, p.EXTI0); - static IRQ_SIGNAL: Signal<()> = Signal::new(); + static IRQ_SIGNAL: Signal = Signal::new(); let radio_irq = interrupt::take!(SUBGHZ_RADIO); radio_irq.set_handler(|_| { IRQ_SIGNAL.signal(()); From b743d9f48ce7635b720119336c1a711269f304ed Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 05:32:45 +0200 Subject: [PATCH 31/62] Add HIL test for bufferedUart --- embassy-rp/src/uart/buffered.rs | 3 +-- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/uart_buffered.rs | 37 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/rp/src/bin/uart_buffered.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6d395b6f4..9c4fbfeb2 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; use super::*; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 4d6877ccd..70fd95557 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -19,6 +19,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +embedded-io = { version = "0.3.0", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs new file mode 100644 index 000000000..a0a3df8da --- /dev/null +++ b/tests/rp/src/bin/uart_buffered.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let config = Config::default(); + let uart = Uart::new_blocking(uart, tx, rx, config); + + let irq = interrupt::take!(UART0_IRQ); + let tx_buf = &mut [0u8; 32]; + let rx_buf = &mut [0u8; 32]; + let mut state = State::new(); + let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); + + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); + + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 7f16b1cd23f53a429bf074e76254bcf592c0b9cf Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 06:01:18 +0200 Subject: [PATCH 32/62] Add blocking API to FirmwareUpdater, and allow for a split prepare/write api --- embassy-boot/boot/src/lib.rs | 186 +++++++++++++++++++++++++++++++++-- 1 file changed, 179 insertions(+), 7 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 96878ace9..1c4d2d47f 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -660,12 +660,6 @@ impl FirmwareUpdater { ) -> Result<(), F::Error> { assert!(data.len() >= F::ERASE_SIZE); - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.dfu.from + offset, - data.len() - ); - flash .erase( (self.dfu.from + offset) as u32, @@ -679,7 +673,141 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - let mut write_offset = self.dfu.from + offset; + FirmwareWriter(self) + .write_firmware(offset, data, flash, block_size) + .await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub fn prepare_update(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self)) + } + + // + // Blocking API + // + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_updated_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + } + + fn set_magic_blocking( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), F::Error> { + flash.read(self.state.from as u32, aligned)?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned)?; + flash.erase(self.state.from as u32, self.state.to as u32)?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned)?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + assert!(data.len() >= F::ERASE_SIZE); + + flash.erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + )?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware_blocking` allows for an optimized + /// API in exchange for added complexity. + pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self)) + } +} + +/// FirmwareWriter allows writing blocks to an already erased flash. +pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater); + +impl<'a> FirmwareWriter<'a> { + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.dfu.from + offset, + data.len() + ); + + let mut write_offset = self.0.dfu.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk).await?; @@ -702,6 +830,50 @@ impl FirmwareUpdater { Ok(()) } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.dfu.from + offset, + data.len() + ); + + let mut write_offset = self.0.dfu.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk)?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } } #[cfg(test)] From b2a327a85884f822d011964bcd44b463b301467f Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 06:53:40 +0200 Subject: [PATCH 33/62] Add get_state helpers to allow self-testing before calling mark_booted --- embassy-boot/boot/src/lib.rs | 56 +++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 1c4d2d47f..6f22d08ea 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -604,6 +604,21 @@ impl FirmwareUpdater { self.dfu.len() } + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + flash.read(self.state.from as u32, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -673,8 +688,8 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - FirmwareWriter(self) - .write_firmware(offset, data, flash, block_size) + FirmwareWriter(self.dfu) + .write_block(offset, data, flash, block_size) .await?; Ok(()) @@ -690,13 +705,28 @@ impl FirmwareUpdater { trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - Ok(FirmwareWriter(self)) + Ok(FirmwareWriter(self.dfu)) } // // Blocking API // + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + flash.read(self.state.from as u32, aligned)?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -764,7 +794,7 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?; + FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; Ok(()) } @@ -779,14 +809,14 @@ impl FirmwareUpdater { trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - Ok(FirmwareWriter(self)) + Ok(FirmwareWriter(self.dfu)) } } /// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater); +pub struct FirmwareWriter(Partition); -impl<'a> FirmwareWriter<'a> { +impl FirmwareWriter { /// Write data to a flash page. /// /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. @@ -794,7 +824,7 @@ impl<'a> FirmwareWriter<'a> { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( + pub async fn write_block( &mut self, offset: usize, data: &[u8], @@ -803,11 +833,11 @@ impl<'a> FirmwareWriter<'a> { ) -> Result<(), F::Error> { trace!( "Writing firmware at offset 0x{:x} len {}", - self.0.dfu.from + offset, + self.0.from + offset, data.len() ); - let mut write_offset = self.0.dfu.from + offset; + let mut write_offset = self.0.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk).await?; @@ -838,7 +868,7 @@ impl<'a> FirmwareWriter<'a> { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( + pub fn write_block_blocking( &mut self, offset: usize, data: &[u8], @@ -847,11 +877,11 @@ impl<'a> FirmwareWriter<'a> { ) -> Result<(), F::Error> { trace!( "Writing firmware at offset 0x{:x} len {}", - self.0.dfu.from + offset, + self.0.from + offset, data.len() ); - let mut write_offset = self.0.dfu.from + offset; + let mut write_offset = self.0.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk)?; From 6fa74b0c022c41c9ac6dd0b937ef402846cbdfae Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 10:36:21 +0200 Subject: [PATCH 34/62] make prepare_update async --- embassy-boot/boot/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 6f22d08ea..8286601ec 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -700,8 +700,8 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub fn prepare_update(&mut self, flash: &mut F) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + pub async fn prepare_update(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); From fa7781c48d9e6687914e674aba63291225ee8d17 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 11:57:26 +0200 Subject: [PATCH 35/62] Add credits to rp-hal --- embassy-rp/src/intrinsics.rs | 3 +++ embassy-rp/src/rom_data.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 9e6624cf0..ac1f54800 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -1,5 +1,8 @@ #![macro_use] +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs + /// Generate a series of aliases for an intrinsic function. macro_rules! intrinsics_aliases { ( diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 93a3632a5..757a27114 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -8,6 +8,9 @@ //! > functionality that would otherwise have to take up space in most user //! > binaries. +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs + /// A bootrom function table code. pub type RomFnTableCode = [u8; 2]; From 7f7c14b7bce5b84eb27c8122535a96a6f0e5dd77 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 12:29:27 +0200 Subject: [PATCH 36/62] usb: split driver trait to separate crate. --- embassy-nrf/src/usb.rs | 17 +-- embassy-rp/src/usb.rs | 27 ++--- embassy-stm32/src/usb/usb.rs | 35 +++--- embassy-usb-driver/Cargo.toml | 16 +++ .../src/lib.rs | 107 +++++++++++++++++- embassy-usb/Cargo.toml | 4 + embassy-usb/src/builder.rs | 11 +- embassy-usb/src/control.rs | 5 +- embassy-usb/src/descriptor.rs | 7 +- embassy-usb/src/descriptor_reader.rs | 2 +- embassy-usb/src/lib.rs | 18 +-- embassy-usb/src/types.rs | 105 ----------------- 12 files changed, 189 insertions(+), 165 deletions(-) create mode 100644 embassy-usb-driver/Cargo.toml rename embassy-usb/src/driver.rs => embassy-usb-driver/src/lib.rs (72%) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 0685d419c..20510eb49 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -10,8 +10,9 @@ use cortex_m::peripheral::NVIC; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; -use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; @@ -243,7 +244,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> interval: u8, ) -> Result { let index = self.alloc_in.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); + let ep_addr = EndpointAddress::from_parts(index, Direction::In); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, ep_type, @@ -259,7 +260,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> interval: u8, ) -> Result { let index = self.alloc_out.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); + let ep_addr = EndpointAddress::from_parts(index, Direction::Out); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, ep_type, @@ -428,8 +429,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { let regs = T::regs(); let i = ep_addr.index(); match ep_addr.direction() { - UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(), - UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(), + Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(), + Direction::In => regs.halted.epin[i].read().getstatus().is_halted(), } } @@ -442,7 +443,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { let mut was_enabled = false; regs.epinen.modify(|r, w| { let mut bits = r.bits(); @@ -466,7 +467,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { In::waker(i).wake(); } - UsbDirection::Out => { + Direction::Out => { regs.epouten.modify(|r, w| { let mut bits = r.bits(); if enabled { diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index a7ec5fb79..ce473b21d 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -7,8 +7,9 @@ use core::task::Poll; use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; @@ -204,8 +205,8 @@ impl<'d, T: Instance> Driver<'d, T> { ); let alloc = match D::dir() { - UsbDirection::Out => &mut self.ep_out, - UsbDirection::In => &mut self.ep_in, + Direction::Out => &mut self.ep_out, + Direction::In => &mut self.ep_in, }; let index = alloc.iter_mut().enumerate().find(|(i, ep)| { @@ -254,7 +255,7 @@ impl<'d, T: Instance> Driver<'d, T> { }; match D::dir() { - UsbDirection::Out => unsafe { + Direction::Out => unsafe { T::dpram().ep_out_control(index - 1).write(|w| { w.set_enable(false); w.set_buffer_address(addr); @@ -262,7 +263,7 @@ impl<'d, T: Instance> Driver<'d, T> { w.set_endpoint_type(ep_type_reg); }) }, - UsbDirection::In => unsafe { + Direction::In => unsafe { T::dpram().ep_in_control(index - 1).write(|w| { w.set_enable(false); w.set_buffer_address(addr); @@ -429,14 +430,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let n = ep_addr.index(); match ep_addr.direction() { - UsbDirection::In => unsafe { + Direction::In => unsafe { T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| { w.set_pid(0, true); // first packet is DATA0, but PID is flipped before }); EP_IN_WAKERS[n].wake(); }, - UsbDirection::Out => unsafe { + Direction::Out => unsafe { T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| { @@ -474,14 +475,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } trait Dir { - fn dir() -> UsbDirection; + fn dir() -> Direction; fn waker(i: usize) -> &'static AtomicWaker; } pub enum In {} impl Dir for In { - fn dir() -> UsbDirection { - UsbDirection::In + fn dir() -> Direction { + Direction::In } #[inline] @@ -492,8 +493,8 @@ impl Dir for In { pub enum Out {} impl Dir for Out { - fn dir() -> UsbDirection { - UsbDirection::Out + fn dir() -> Direction { + Direction::Out } #[inline] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index e5ee1181c..39809a3e1 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -9,8 +9,9 @@ use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; -use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; @@ -279,8 +280,8 @@ impl<'d, T: Instance> Driver<'d, T> { } let used = ep.used_out || ep.used_in; let used_dir = match D::dir() { - UsbDirection::Out => ep.used_out, - UsbDirection::In => ep.used_in, + Direction::Out => ep.used_out, + Direction::In => ep.used_in, }; !used || (ep.ep_type == ep_type && !used_dir) }); @@ -293,7 +294,7 @@ impl<'d, T: Instance> Driver<'d, T> { ep.ep_type = ep_type; let buf = match D::dir() { - UsbDirection::Out => { + Direction::Out => { assert!(!ep.used_out); ep.used_out = true; @@ -312,7 +313,7 @@ impl<'d, T: Instance> Driver<'d, T> { _phantom: PhantomData, } } - UsbDirection::In => { + Direction::In => { assert!(!ep.used_in); ep.used_in = true; @@ -504,7 +505,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { // This can race, so do a retry loop. let reg = T::regs().epr(ep_addr.index() as _); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { loop { let r = unsafe { reg.read() }; match r.stat_tx() { @@ -523,7 +524,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } EP_IN_WAKERS[ep_addr.index()].wake(); } - UsbDirection::Out => { + Direction::Out => { loop { let r = unsafe { reg.read() }; match r.stat_rx() { @@ -549,8 +550,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let regs = T::regs(); let epr = unsafe { regs.epr(ep_addr.index() as _).read() }; match ep_addr.direction() { - UsbDirection::In => epr.stat_tx() == Stat::STALL, - UsbDirection::Out => epr.stat_rx() == Stat::STALL, + Direction::In => epr.stat_tx() == Stat::STALL, + Direction::Out => epr.stat_rx() == Stat::STALL, } } @@ -560,7 +561,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let reg = T::regs().epr(ep_addr.index() as _); trace!("EPR before: {:04x}", unsafe { reg.read() }.0); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { loop { let want_stat = match enabled { false => Stat::DISABLED, @@ -576,7 +577,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } EP_IN_WAKERS[ep_addr.index()].wake(); } - UsbDirection::Out => { + Direction::Out => { loop { let want_stat = match enabled { false => Stat::DISABLED, @@ -616,14 +617,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } trait Dir { - fn dir() -> UsbDirection; + fn dir() -> Direction; fn waker(i: usize) -> &'static AtomicWaker; } pub enum In {} impl Dir for In { - fn dir() -> UsbDirection { - UsbDirection::In + fn dir() -> Direction { + Direction::In } #[inline] @@ -634,8 +635,8 @@ impl Dir for In { pub enum Out {} impl Dir for Out { - fn dir() -> UsbDirection { - UsbDirection::Out + fn dir() -> Direction { + Direction::Out } #[inline] diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml new file mode 100644 index 000000000..b525df337 --- /dev/null +++ b/embassy-usb-driver/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "embassy-usb-driver" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-driver-v$VERSION/embassy-usb/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-driver/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } \ No newline at end of file diff --git a/embassy-usb/src/driver.rs b/embassy-usb-driver/src/lib.rs similarity index 72% rename from embassy-usb/src/driver.rs rename to embassy-usb-driver/src/lib.rs index 7888f1639..051190a48 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,111 @@ +#![no_std] + use core::future::Future; -use super::types::*; +/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from +/// the perspective of the host, which is backward for devices, but the standard directions are used +/// for consistency. +/// +/// The values of the enum also match the direction bit used in endpoint addresses and control +/// request types. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Direction { + /// Host to device (OUT) + Out = 0x00, + /// Device to host (IN) + In = 0x80, +} + +impl From for Direction { + fn from(value: u8) -> Self { + unsafe { core::mem::transmute(value & 0x80) } + } +} + +/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the +/// transfer bmAttributes transfer type bits. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum EndpointType { + /// Control endpoint. Used for device management. Only the host can initiate requests. Usually + /// used only endpoint 0. + Control = 0b00, + /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. + Isochronous = 0b01, + /// Bulk endpoint. Used for large amounts of best-effort reliable data. + Bulk = 0b10, + /// Interrupt endpoint. Used for small amounts of time-critical reliable data. + Interrupt = 0b11, +} + +/// Type-safe endpoint address. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EndpointAddress(u8); + +impl From for EndpointAddress { + #[inline] + fn from(addr: u8) -> EndpointAddress { + EndpointAddress(addr) + } +} + +impl From for u8 { + #[inline] + fn from(addr: EndpointAddress) -> u8 { + addr.0 + } +} + +impl EndpointAddress { + const INBITS: u8 = Direction::In as u8; + + /// Constructs a new EndpointAddress with the given index and direction. + #[inline] + pub fn from_parts(index: usize, dir: Direction) -> Self { + EndpointAddress(index as u8 | dir as u8) + } + + /// Gets the direction part of the address. + #[inline] + pub fn direction(&self) -> Direction { + if (self.0 & Self::INBITS) != 0 { + Direction::In + } else { + Direction::Out + } + } + + /// Returns true if the direction is IN, otherwise false. + #[inline] + pub fn is_in(&self) -> bool { + (self.0 & Self::INBITS) != 0 + } + + /// Returns true if the direction is OUT, otherwise false. + #[inline] + pub fn is_out(&self) -> bool { + (self.0 & Self::INBITS) == 0 + } + + /// Gets the index part of the endpoint address. + #[inline] + pub fn index(&self) -> usize { + (self.0 & !Self::INBITS) as usize + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EndpointInfo { + pub addr: EndpointAddress, + pub ep_type: EndpointType, + pub max_packet_size: u16, + pub interval: u8, +} /// Driver for a specific USB peripheral. Implement this to add support for a new hardware /// platform. diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 8cad4d314..660ecc8cc 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -9,8 +9,12 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/s features = ["defmt"] target = "thumbv7em-none-eabi" +[features] +defmt = ["dep:defmt", "embassy-usb-driver/defmt"] + [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6be88bc76..87a8333bb 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,11 +1,10 @@ use heapless::Vec; -use super::control::ControlHandler; -use super::descriptor::{BosWriter, DescriptorWriter}; -use super::driver::{Driver, Endpoint}; -use super::types::*; -use super::{DeviceStateHandler, UsbDevice, MAX_INTERFACE_COUNT}; -use crate::{Interface, STRING_INDEX_CUSTOM_START}; +use crate::control::ControlHandler; +use crate::descriptor::{BosWriter, DescriptorWriter}; +use crate::driver::{Driver, Endpoint, EndpointType}; +use crate::types::*; +use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 3e5749a01..9e0dee888 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -1,7 +1,8 @@ //! USB control data types. use core::mem; -use super::types::*; +use crate::driver::Direction; +use crate::types::StringIndex; /// Control request type. #[repr(u8)] @@ -42,7 +43,7 @@ pub enum Recipient { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Request { /// Direction of the request. - pub direction: UsbDirection, + pub direction: Direction, /// Type of the request. pub request_type: RequestType, /// Recipient of the request. diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index b94a4b161..497f03196 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,6 +1,7 @@ -use super::builder::Config; -use super::types::*; -use super::CONFIGURATION_VALUE; +use crate::builder::Config; +use crate::driver::EndpointInfo; +use crate::types::*; +use crate::CONFIGURATION_VALUE; /// Standard descriptor types #[allow(missing_docs)] diff --git a/embassy-usb/src/descriptor_reader.rs b/embassy-usb/src/descriptor_reader.rs index 0a12b566c..d64bcb73b 100644 --- a/embassy-usb/src/descriptor_reader.rs +++ b/embassy-usb/src/descriptor_reader.rs @@ -1,5 +1,5 @@ use crate::descriptor::descriptor_type; -use crate::types::EndpointAddress; +use crate::driver::EndpointAddress; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 6f58c953c..e1a99cfae 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -4,23 +4,23 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub use embassy_usb_driver as driver; + mod builder; pub mod control; pub mod descriptor; mod descriptor_reader; -pub mod driver; pub mod types; use embassy_futures::select::{select, Either}; use heapless::Vec; -pub use self::builder::{Builder, Config}; -use self::control::*; -use self::descriptor::*; -use self::driver::{Bus, Driver, Event}; -use self::types::*; +pub use crate::builder::{Builder, Config}; +use crate::control::*; +use crate::descriptor::*; use crate::descriptor_reader::foreach_endpoint; -use crate::driver::ControlPipe; +use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event}; +use crate::types::*; /// The global state of the USB device. /// @@ -250,8 +250,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { trace!("control request: {:?}", req); match req.direction { - UsbDirection::In => self.handle_control_in(req).await, - UsbDirection::Out => self.handle_control_out(req).await, + Direction::In => self.handle_control_in(req).await, + Direction::Out => self.handle_control_out(req).await, } if self.inner.set_address_pending { diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index b8717ffa9..aeab063d1 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,108 +1,3 @@ -/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from -/// the perspective of the host, which is backward for devices, but the standard directions are used -/// for consistency. -/// -/// The values of the enum also match the direction bit used in endpoint addresses and control -/// request types. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum UsbDirection { - /// Host to device (OUT) - Out = 0x00, - /// Device to host (IN) - In = 0x80, -} - -impl From for UsbDirection { - fn from(value: u8) -> Self { - unsafe { core::mem::transmute(value & 0x80) } - } -} - -/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the -/// transfer bmAttributes transfer type bits. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum EndpointType { - /// Control endpoint. Used for device management. Only the host can initiate requests. Usually - /// used only endpoint 0. - Control = 0b00, - /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. - Isochronous = 0b01, - /// Bulk endpoint. Used for large amounts of best-effort reliable data. - Bulk = 0b10, - /// Interrupt endpoint. Used for small amounts of time-critical reliable data. - Interrupt = 0b11, -} - -/// Type-safe endpoint address. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EndpointAddress(u8); - -impl From for EndpointAddress { - #[inline] - fn from(addr: u8) -> EndpointAddress { - EndpointAddress(addr) - } -} - -impl From for u8 { - #[inline] - fn from(addr: EndpointAddress) -> u8 { - addr.0 - } -} - -impl EndpointAddress { - const INBITS: u8 = UsbDirection::In as u8; - - /// Constructs a new EndpointAddress with the given index and direction. - #[inline] - pub fn from_parts(index: usize, dir: UsbDirection) -> Self { - EndpointAddress(index as u8 | dir as u8) - } - - /// Gets the direction part of the address. - #[inline] - pub fn direction(&self) -> UsbDirection { - if (self.0 & Self::INBITS) != 0 { - UsbDirection::In - } else { - UsbDirection::Out - } - } - - /// Returns true if the direction is IN, otherwise false. - #[inline] - pub fn is_in(&self) -> bool { - (self.0 & Self::INBITS) != 0 - } - - /// Returns true if the direction is OUT, otherwise false. - #[inline] - pub fn is_out(&self) -> bool { - (self.0 & Self::INBITS) == 0 - } - - /// Gets the index part of the endpoint address. - #[inline] - pub fn index(&self) -> usize { - (self.0 & !Self::INBITS) as usize - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EndpointInfo { - pub addr: EndpointAddress, - pub ep_type: EndpointType, - pub max_packet_size: u16, - pub interval: u8, -} - /// A handle for a USB interface that contains its number. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From f4f58249722bc656a13865e06535d208440c3e4a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 12:35:33 +0200 Subject: [PATCH 37/62] usb: do not allow converting Directon to/from u8 --- embassy-usb-driver/src/lib.rs | 11 ++--------- embassy-usb/src/control.rs | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 051190a48..fc29786fc 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -8,20 +8,13 @@ use core::future::Future; /// /// The values of the enum also match the direction bit used in endpoint addresses and control /// request types. -#[repr(u8)] #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Direction { /// Host to device (OUT) - Out = 0x00, + Out, /// Device to host (IN) - In = 0x80, -} - -impl From for Direction { - fn from(value: u8) -> Self { - unsafe { core::mem::transmute(value & 0x80) } - } + In, } /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 9e0dee888..d6d0c6565 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -106,7 +106,7 @@ impl Request { let recipient = rt & 0b11111; Request { - direction: rt.into(), + direction: if rt & 0x80 == 0 { Direction::Out } else { Direction::In }, request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, recipient: if recipient <= 3 { unsafe { mem::transmute(recipient) } From f27a47a37b59bf3b9079f4d4d5f43caf7b7872f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 13:00:21 +0200 Subject: [PATCH 38/62] usb: move classes into the `embassy-usb` crate. --- embassy-usb-hid/Cargo.toml | 24 -- embassy-usb-hid/src/fmt.rs | 225 ------------------ embassy-usb-ncm/Cargo.toml | 17 -- embassy-usb-ncm/src/fmt.rs | 225 ------------------ embassy-usb-serial/Cargo.toml | 17 -- embassy-usb-serial/src/fmt.rs | 225 ------------------ embassy-usb/Cargo.toml | 9 +- .../src/class/cdc_acm.rs | 15 +- .../src/class/cdc_ncm.rs | 13 +- .../lib.rs => embassy-usb/src/class/hid.rs | 23 +- embassy-usb/src/class/mod.rs | 3 + embassy-usb/src/lib.rs | 1 + examples/nrf/Cargo.toml | 5 +- examples/nrf/src/bin/usb_ethernet.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 4 +- examples/nrf/src/bin/usb_hid_mouse.rs | 4 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/nrf/src/bin/usb_serial_multitask.rs | 2 +- examples/rp/Cargo.toml | 2 - examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 - examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 2 - examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 3 - examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 4 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- 29 files changed, 45 insertions(+), 795 deletions(-) delete mode 100644 embassy-usb-hid/Cargo.toml delete mode 100644 embassy-usb-hid/src/fmt.rs delete mode 100644 embassy-usb-ncm/Cargo.toml delete mode 100644 embassy-usb-ncm/src/fmt.rs delete mode 100644 embassy-usb-serial/Cargo.toml delete mode 100644 embassy-usb-serial/src/fmt.rs rename embassy-usb-serial/src/lib.rs => embassy-usb/src/class/cdc_acm.rs (96%) rename embassy-usb-ncm/src/lib.rs => embassy-usb/src/class/cdc_ncm.rs (97%) rename embassy-usb-hid/src/lib.rs => embassy-usb/src/class/hid.rs (96%) create mode 100644 embassy-usb/src/class/mod.rs diff --git a/embassy-usb-hid/Cargo.toml b/embassy-usb-hid/Cargo.toml deleted file mode 100644 index 2f7733dc6..000000000 --- a/embassy-usb-hid/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "embassy-usb-hid" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-hid-v$VERSION/embassy-usb-hid/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-hid/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[features] -default = ["usbd-hid"] -usbd-hid = ["dep:usbd-hid", "ssmarshal"] - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } -usbd-hid = { version = "0.6.0", optional = true } -ssmarshal = { version = "1.0", default-features = false, optional = true } -futures-util = { version = "0.3.21", default-features = false } diff --git a/embassy-usb-hid/src/fmt.rs b/embassy-usb-hid/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-usb-hid/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy-usb-ncm/Cargo.toml b/embassy-usb-ncm/Cargo.toml deleted file mode 100644 index 15d3db96f..000000000 --- a/embassy-usb-ncm/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "embassy-usb-ncm" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-ncm-v$VERSION/embassy-usb-ncm/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-ncm/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb-ncm/src/fmt.rs b/embassy-usb-ncm/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-usb-ncm/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy-usb-serial/Cargo.toml b/embassy-usb-serial/Cargo.toml deleted file mode 100644 index 9788588e9..000000000 --- a/embassy-usb-serial/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "embassy-usb-serial" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-serial-v$VERSION/embassy-usb-serial/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-serial/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb-serial/src/fmt.rs b/embassy-usb-serial/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-usb-serial/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 660ecc8cc..aad54dbaf 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -11,11 +11,18 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] +usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] +default = ["usbd-hid"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -heapless = "0.7.10" \ No newline at end of file +heapless = "0.7.10" + +# for HID +usbd-hid = { version = "0.6.0", optional = true } +ssmarshal = { version = "1.0", default-features = false, optional = true } diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb/src/class/cdc_acm.rs similarity index 96% rename from embassy-usb-serial/src/lib.rs rename to embassy-usb/src/class/cdc_acm.rs index 15c2bb0a7..09bb1cc8d 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,18 +1,13 @@ -#![no_std] -#![feature(type_alias_impl_trait)] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::cell::Cell; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::blocking_mutex::CriticalSectionMutex; -use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::types::*; -use embassy_usb::Builder; + +use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::types::*; +use crate::Builder; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb-ncm/src/lib.rs b/embassy-usb/src/class/cdc_ncm.rs similarity index 97% rename from embassy-usb-ncm/src/lib.rs rename to embassy-usb/src/class/cdc_ncm.rs index e796af28f..a39b87e9b 100644 --- a/embassy-usb-ncm/src/lib.rs +++ b/embassy-usb/src/class/cdc_ncm.rs @@ -1,15 +1,10 @@ -#![no_std] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; -use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::types::*; -use embassy_usb::Builder; +use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::types::*; +use crate::Builder; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb/src/class/hid.rs similarity index 96% rename from embassy-usb-hid/src/lib.rs rename to embassy-usb/src/class/hid.rs index 8b181aec8..4d1fa995f 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb/src/class/hid.rs @@ -1,23 +1,16 @@ -#![no_std] -#![feature(type_alias_impl_trait)] - -//! Implements HID functionality for a usb-device device. - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::mem::MaybeUninit; use core::ops::Range; use core::sync::atomic::{AtomicUsize, Ordering}; -use embassy_usb::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::Builder; #[cfg(feature = "usbd-hid")] use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; +use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::Builder; + const USB_CLASS_HID: u8 = 0x03; const USB_SUBCLASS_NONE: u8 = 0x00; const USB_PROTOCOL_NONE: u8 = 0x00; @@ -204,9 +197,9 @@ pub enum ReadError { Sync(Range), } -impl From for ReadError { - fn from(val: embassy_usb::driver::EndpointError) -> Self { - use embassy_usb::driver::EndpointError::*; +impl From for ReadError { + fn from(val: EndpointError) -> Self { + use EndpointError::*; match val { BufferOverflow => ReadError::BufferOverflow, Disabled => ReadError::Disabled, @@ -437,7 +430,7 @@ impl<'d> ControlHandler for Control<'d> { } } - fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse { + fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { trace!("HID control_out {:?} {=[u8]:x}", req, data); if let RequestType::Class = req.request_type { match req.request { diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs new file mode 100644 index 000000000..af27577a6 --- /dev/null +++ b/embassy-usb/src/class/mod.rs @@ -0,0 +1,3 @@ +pub mod cdc_acm; +pub mod cdc_ncm; +pub mod hid; diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index e1a99cfae..661b84119 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub use embassy_usb_driver as driver; mod builder; +pub mod class; pub mod control; pub mod descriptor; mod descriptor_reader; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index dbc659cda..a5d340c69 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -15,9 +15,6 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } embedded-io = "0.3.0" defmt = "0.3" diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 33ca380ff..de93a2b45 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -15,8 +15,8 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4eb7d37c9..76e198719 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -14,9 +14,9 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; -use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -67,7 +67,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: KeyboardReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 65fbda1cf..4916a38d4 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -10,9 +10,9 @@ use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; -use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: MouseReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a740b4e0a..7c9c4184b 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -9,9 +9,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index c646c0bbd..93efc2fe6 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -8,9 +8,9 @@ use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_serial::{CdcAcmClass, State}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 24c3cdd67..3c8f923e7 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,9 +10,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-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 166ffe175..1057fe7fd 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -13,8 +13,8 @@ use embassy_rp::usb::Driver; use embassy_rp::{interrupt, peripherals}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index bf92a1636..b7d6493b4 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -7,9 +7,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::interrupt; use embassy_rp::usb::{Driver, Instance}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 895e043dd..e6553789a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -9,7 +9,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a14e728ba..ad92cdeb2 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 27f5c260a..f5b0b880c 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -9,8 +9,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index b9fd20e2b..f6d27c860 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 05945f6bf..9ebab6476 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,9 +11,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index c96a83ead..4f36d3f5a 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -15,8 +15,8 @@ use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::StaticCell; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index fa92ceae3..d38ed7496 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -9,9 +9,9 @@ use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; -use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: MouseReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7484dc832..7562a4e96 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -8,9 +8,9 @@ use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From daf2744716db2ddb2aa97d85c2a91475920b2530 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Mon, 26 Sep 2022 15:28:09 +0200 Subject: [PATCH 39/62] Rework STM32 BufferedUart internals so we can split into Rx and Tx like embassy-nrf --- embassy-stm32/src/usart/buffered.rs | 260 +++++++++++++++++++--------- 1 file changed, 175 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 46c49a997..2a711bc06 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,3 +1,4 @@ +use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::Poll; @@ -29,7 +30,15 @@ unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {} pub struct BufferedUart<'d, T: BasicInstance> { - inner: PeripheralMutex<'d, StateInner<'d, T>>, + inner: RefCell>>, +} + +pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { + inner: &'u BufferedUart<'d, T>, +} + +pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { + inner: &'u BufferedUart<'d, T>, } impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} @@ -53,14 +62,124 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { phantom: PhantomData, tx: RingBuffer::new(tx_buffer), tx_waker: WakerRegistration::new(), rx: RingBuffer::new(rx_buffer), rx_waker: WakerRegistration::new(), - }), + })), + } + } + + pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) { + (BufferedUartRx { inner: self }, BufferedUartTx { inner: self }) + } + + async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { + poll_fn(move |cx| { + let mut do_pend = false; + let mut inner = self.inner.borrow_mut(); + let res = inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + inner.pend(); + } + + res + }) + .await + } + + async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + poll_fn(move |cx| { + let mut inner = self.inner.borrow_mut(); + let (poll, empty) = inner.with(|state| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + inner.pend(); + } + poll + }) + .await + } + + async fn inner_flush<'a>(&'a self) -> Result<(), Error> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + .await + } + + async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.rx.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + .await + } + + fn inner_consume(&self, amt: usize) { + let mut inner = self.inner.borrow_mut(); + let signal = inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + inner.pend(); } } } @@ -155,41 +274,31 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { + type Error = Error; +} + +impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { + type Error = Error; +} + impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { type ReadFuture<'a> = impl Future> where Self: 'a; fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); + self.inner_read(buf) + } +} - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); +impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; - if state.rx.is_full() { - do_pend = true; - } - state.rx.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx_waker.register(cx.waker()); - Poll::Pending - }); - - if do_pend { - self.inner.pend(); - } - - res - }) + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.inner.inner_read(buf) } } @@ -199,34 +308,25 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> Self: 'a; fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) - }) + self.inner_fill_buf() } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - self.inner.pend(); - } + self.inner_consume(amt) + } +} + +impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + self.inner.inner_fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.inner.inner_consume(amt) } } @@ -236,26 +336,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - (Poll::Ready(Ok(n)), empty) - }); - if empty { - self.inner.pend(); - } - poll - }) + self.inner_write(buf) } type FlushFuture<'a> = impl Future> @@ -263,15 +344,24 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + self.inner_flush() + } +} + +impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.inner.inner_write(buf) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + self.inner.inner_flush() } } From 1e95c4fcfff3edf87a6c4cdacb228cb08e6d4e50 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 19:52:55 +0200 Subject: [PATCH 40/62] rp: Disable intrinsics by default. --- ci.sh | 1 + embassy-rp/Cargo.toml | 2 +- embassy-rp/src/intrinsics.rs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ci.sh b/ci.sh index ae1b44281..69440ec36 100755 --- a/ci.sh +++ b/ci.sh @@ -58,6 +58,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \ diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index df0af8dfb..c43fd7e72 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -23,7 +23,7 @@ unstable-pac = [] time-driver = [] rom-func-cache = [] -disable-intrinsics = [] +intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index ac1f54800..3e75fb7fc 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -17,7 +17,7 @@ macro_rules! intrinsics_aliases { $alias:ident $($rest:ident)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { extern $abi fn $alias( $($argname: $ty),* ) -> $ret { $name($($argname),*) @@ -35,7 +35,7 @@ macro_rules! intrinsics_aliases { $alias:ident $($rest:ident)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { $name($($argname),*) @@ -55,7 +55,7 @@ macro_rules! intrinsics_aliases { /// is to abstract anything special that needs to be done to override an /// intrinsic function. Intrinsic generation is disabled for non-ARM targets /// so things like CI and docs generation do not have problems. Additionally -/// they can be disabled with the crate feature `disable-intrinsics` for +/// they can be disabled by disabling the crate feature `intrinsics` for /// testing or comparing performance. /// /// Like the compiler-builtins macro, it accepts a series of functions that @@ -214,13 +214,13 @@ macro_rules! intrinsics { $($rest:tt)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] $(#[$($attr)*])* extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { #[no_mangle] $(#[$($attr)*])* @@ -231,7 +231,7 @@ macro_rules! intrinsics { // Not exported, but defined so the actual implementation is // considered used - #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[cfg(not(all(target_arch = "arm", feature = "intrinsics")))] #[allow(dead_code)] fn $name( $($argname: $ty),* ) -> $ret { $($body)* @@ -248,13 +248,13 @@ macro_rules! intrinsics { $($rest:tt)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] $(#[$($attr)*])* unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { #[no_mangle] $(#[$($attr)*])* @@ -265,7 +265,7 @@ macro_rules! intrinsics { // Not exported, but defined so the actual implementation is // considered used - #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[cfg(not(all(target_arch = "arm", feature = "intrinsics")))] #[allow(dead_code)] unsafe fn $name( $($argname: $ty),* ) -> $ret { $($body)* From 75e93cc142c1c017e94dd28f6f04d3a4cb1ac67b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 20:33:20 +0200 Subject: [PATCH 41/62] rp: enable time-driver in Cargo.toml instead of ci.sh --- ci.sh | 2 +- tests/rp/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index 69440ec36..cd1c0786c 100755 --- a/ci.sh +++ b/ci.sh @@ -121,7 +121,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 11ecb9169..7e2717ddf 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" From ee76831f93e792757bf43136be712c343c4d5336 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:05:12 +0200 Subject: [PATCH 42/62] Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/uart/buffered.rs | 286 ++++++++++++++++++++++++ embassy-rp/src/{uart.rs => uart/mod.rs} | 84 ++++++- 3 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/uart/buffered.rs rename embassy-rp/src/{uart.rs => uart/mod.rs} (87%) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index c43fd7e72..211b6a401 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -52,6 +52,7 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } +embedded-io = { version = "0.3.0", features = ["async"], optional = true } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs new file mode 100644 index 000000000..c31af8018 --- /dev/null +++ b/embassy-rp/src/uart/buffered.rs @@ -0,0 +1,286 @@ +use core::future::Future; +use core::task::Poll; + +use atomic_polyfill::{compiler_fence, Ordering}; +use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_hal_common::ring_buffer::RingBuffer; +use embassy_sync::waitqueue::WakerRegistration; +use futures::future::poll_fn; + +use super::*; + +pub struct State<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> State<'d, T> { + pub fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct StateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + rx_waker: WakerRegistration, + rx: RingBuffer<'d>, + + tx_waker: WakerRegistration, + tx: RingBuffer<'d>, +} + +unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} + +pub struct BufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, StateInner<'d, T>>, +} + +impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} + +impl<'d, T: Instance> BufferedUart<'d, T> { + pub fn new( + state: &'d mut State<'d, T>, + _uart: Uart<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + ) -> BufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + phantom: PhantomData, + tx: RingBuffer::new(tx_buffer), + tx_waker: WakerRegistration::new(), + + rx: RingBuffer::new(rx_buffer), + rx_waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> StateInner<'d, T> +where + Self: 'd, +{ + fn on_rx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.rxris() { + if ris.peris() { + warn!("Parity error"); + } + if ris.feris() { + warn!("Framing error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + let buf = self.rx.push_buf(); + if !buf.is_empty() { + buf[0] = r.uartdr().read().data(); + self.rx.push(1); + } else { + warn!("RX buffer full, discard received byte"); + } + + if self.rx.is_full() { + self.rx_waker.wake(); + } + } + + if ris.rtris() { + self.rx_waker.wake(); + }; + } + } + + fn on_tx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rtic(true); + }); + + if ris.txris() { + let buf = self.tx.pop_buf(); + if !buf.is_empty() { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + r.uartdr().write(|w| w.set_data(buf[0].into())); + self.tx.pop(1); + self.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); + } + } + } + } +} + +impl<'d, T: Instance> PeripheralState for StateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.on_rx(); + self.on_tx(); + } +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.rx.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let (poll, empty) = self.inner.with(|state| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart/mod.rs similarity index 87% rename from embassy-rp/src/uart.rs rename to embassy-rp/src/uart/mod.rs index 987b716b4..3b71d87be 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart/mod.rs @@ -475,6 +475,76 @@ mod eh1 { impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + } #[cfg(all( @@ -532,6 +602,12 @@ mod eha { } } +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::*; + + mod sealed { use super::*; @@ -541,6 +617,8 @@ mod sealed { const TX_DREQ: u8; const RX_DREQ: u8; + type Interrupt: crate::interrupt::Interrupt; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} @@ -571,6 +649,8 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { pac::$inst @@ -580,8 +660,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0, 20, 21); -impl_instance!(UART1, UART1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} From f2239d34cc26bb147d136d312b8b6af1020d4e0f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:36:27 +0200 Subject: [PATCH 43/62] Add bufferedUart, including a split version for only Rx or Tx --- embassy-rp/src/uart/buffered.rs | 379 ++++++++++++++++++++++++++------ embassy-rp/src/uart/mod.rs | 2 +- 2 files changed, 315 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c31af8018..3eb96e3d5 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -9,31 +9,70 @@ use futures::future::poll_fn; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); +pub struct State<'d, T: Instance>(StateStorage>); impl<'d, T: Instance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } -struct StateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, +pub struct RxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> RxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } } -unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} +pub struct TxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> TxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct RxStateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + waker: WakerRegistration, + buf: RingBuffer<'d>, +} + +struct TxStateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + waker: WakerRegistration, + buf: RingBuffer<'d>, +} + +struct FullStateInner<'d, T: Instance> { + rx: RxStateInner<'d, T>, + tx: TxStateInner<'d, T>, +} + +unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {} + +unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {} + +unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {} pub struct BufferedUart<'d, T: Instance> { - inner: PeripheralMutex<'d, StateInner<'d, T>>, + inner: PeripheralMutex<'d, FullStateInner<'d, T>>, +} + +pub struct RxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, RxStateInner<'d, T>>, +} + +pub struct TxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for TxBufferedUart<'d, T> {} impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( @@ -55,66 +94,158 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), - - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), + inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner { + tx: TxStateInner { + phantom: PhantomData, + waker: WakerRegistration::new(), + buf: RingBuffer::new(tx_buffer), + }, + rx: RxStateInner { + phantom: PhantomData, + waker: WakerRegistration::new(), + buf: RingBuffer::new(rx_buffer), + }, }), } } } -impl<'d, T: Instance> StateInner<'d, T> +impl<'d, T: Instance> RxBufferedUart<'d, T> { + pub fn new( + state: &'d mut RxState<'d, T>, + _uart: UartRx<'d, T, M>, + irq: impl Peripheral

+ 'd, + rx_buffer: &'d mut [u8], + ) -> RxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(rx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> TxBufferedUart<'d, T> { + pub fn new( + state: &'d mut TxState<'d, T>, + _uart: UartTx<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + ) -> TxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(tx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> where Self: 'd, { - fn on_rx(&mut self) { + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.rx.on_interrupt(); + self.tx.on_interrupt(); + } +} + +impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartris().read(); + let ris = r.uartmis().read(); // Clear interrupt flags - r.uarticr().write(|w| { + r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxris() { - if ris.peris() { + if ris.rxmis() { + if ris.pemis() { warn!("Parity error"); + r.uarticr().modify(|w| { + w.set_peic(true); + }); } - if ris.feris() { + if ris.femis() { warn!("Framing error"); + r.uarticr().modify(|w| { + w.set_feic(true); + }); } - if ris.beris() { + if ris.bemis() { warn!("Break error"); + r.uarticr().modify(|w| { + w.set_beic(true); + }); } - if ris.oeris() { + if ris.oemis() { warn!("Overrun error"); + r.uarticr().modify(|w| { + w.set_oeic(true); + }); } - let buf = self.rx.push_buf(); + let buf = self.buf.push_buf(); if !buf.is_empty() { buf[0] = r.uartdr().read().data(); - self.rx.push(1); + self.buf.push(1); } else { warn!("RX buffer full, discard received byte"); } - if self.rx.is_full() { - self.rx_waker.wake(); + if self.buf.is_full() { + self.waker.wake(); } } - if ris.rtris() { - self.rx_waker.wake(); + if ris.rtmis() { + self.waker.wake(); }; } } +} - fn on_tx(&mut self) { +impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { let r = T::regs(); unsafe { let ris = r.uartris().read(); @@ -124,14 +255,14 @@ where }); if ris.txris() { - let buf = self.tx.pop_buf(); + let buf = self.buf.pop_buf(); if !buf.is_empty() { r.uartimsc().modify(|w| { w.set_txim(true); }); r.uartdr().write(|w| w.set_data(buf[0].into())); - self.tx.pop(1); - self.tx_waker.wake(); + self.buf.pop(1); + self.waker.wake(); } else { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { @@ -143,17 +274,6 @@ where } } -impl<'d, T: Instance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); - } -} - impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other @@ -164,8 +284,16 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { + type Error = Error; +} + impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> where Self: 'a; @@ -176,20 +304,58 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); + let data = state.rx.buf.pop_buf(); if !data.is_empty() { let len = data.len().min(buf.len()); buf[..len].copy_from_slice(&data[..len]); - if state.rx.is_full() { + if state.rx.buf.is_full() { do_pend = true; } - state.rx.pop(len); + state.rx.buf.pop(len); return Poll::Ready(Ok(len)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.buf.is_full() { + do_pend = true; + } + state.buf.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.waker.register(cx.waker()); Poll::Pending }); @@ -213,7 +379,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); + let buf = state.rx.buf.pop_buf(); if !buf.is_empty() { let buf: &[u8] = buf; // Safety: buffer lives as long as uart @@ -221,7 +387,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> return Poll::Ready(Ok(buf)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); Poll::>::Pending }) }) @@ -229,8 +395,45 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> fn consume(&mut self, amt: usize) { let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); + let full = state.rx.buf.is_full(); + state.rx.buf.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.buf.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.buf.is_full(); + state.buf.pop(amt); full }); if signal { @@ -247,16 +450,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); + let empty = state.tx.buf.is_empty(); + let tx_buf = state.tx.buf.push_buf(); if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); + state.tx.waker.register(cx.waker()); return (Poll::Pending, empty); } let n = core::cmp::min(tx_buf.len(), buf.len()); tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + state.tx.buf.push(n); (Poll::Ready(Ok(n)), empty) }); @@ -274,8 +477,54 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { poll_fn(move |cx| { self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); + if !state.tx.buf.is_empty() { + state.tx.waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let (poll, empty) = self.inner.with(|state| { + let empty = state.buf.is_empty(); + let tx_buf = state.buf.push_buf(); + if tx_buf.is_empty() { + state.waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.buf.is_empty() { + state.waker.register(cx.waker()); return Poll::Pending; } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 3b71d87be..67e24b605 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(true); + w.set_fen(false); }); r.uartcr().write(|w| { From d6af0f62860f79f790d6d1e042f36c8623328ab1 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:49:47 +0200 Subject: [PATCH 44/62] Formatting --- embassy-rp/src/uart/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 67e24b605..76ecdf7ac 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -544,7 +544,6 @@ mod eh1 { self.blocking_flush().map_err(nb::Error::Other) } } - } #[cfg(all( @@ -607,7 +606,6 @@ mod buffered; #[cfg(feature = "nightly")] pub use buffered::*; - mod sealed { use super::*; @@ -649,7 +647,7 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { From 1db9e464ff13a05d1267bb33fde490bcce35af5a Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:28:35 +0200 Subject: [PATCH 45/62] Enable embedded-io on nightly --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 211b6a401..d0cf8025c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -27,7 +27,7 @@ intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. From b3dfd06dd6da3369813cf469a7fcd87c22047e87 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 21 Sep 2022 06:00:35 +0200 Subject: [PATCH 46/62] Remove code-duplication in async bufferedUart implementations --- embassy-rp/src/uart/buffered.rs | 215 +++++++++++++------------------- 1 file changed, 89 insertions(+), 126 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 3eb96e3d5..6d395b6f4 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,5 +1,5 @@ use core::future::Future; -use core::task::Poll; +use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; @@ -87,9 +87,9 @@ impl<'d, T: Instance> BufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); + w.set_txim(true); }); } @@ -122,7 +122,6 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); }); @@ -151,9 +150,7 @@ impl<'d, T: Instance> TxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? - w.set_rxim(true); - w.set_rtim(true); + w.set_txim(true); }); } @@ -179,6 +176,51 @@ where } } +impl<'d, T: Instance> RxStateInner<'d, T> +where + Self: 'd, +{ + fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll>, bool) { + // We have data ready in buffer? Return it. + let mut do_pend = false; + let data = self.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if self.buf.is_full() { + do_pend = true; + } + self.buf.pop(len); + + return (Poll::Ready(Ok(len)), do_pend); + } + + self.waker.register(waker); + (Poll::Pending, do_pend) + } + + fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll> { + // We have data ready in buffer? Return it. + let buf = self.buf.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + self.waker.register(waker); + Poll::Pending + } + + fn consume(&mut self, amt: usize) -> bool { + let full = self.buf.is_full(); + self.buf.pop(amt); + full + } +} + impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> where Self: 'd, @@ -240,6 +282,35 @@ where } } +impl<'d, T: Instance> TxStateInner<'d, T> +where + Self: 'd, +{ + fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll>, bool) { + let empty = self.buf.is_empty(); + let tx_buf = self.buf.push_buf(); + if tx_buf.is_empty() { + self.waker.register(waker); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + self.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + } + + fn flush(&mut self, waker: &Waker) -> Poll> { + if !self.buf.is_empty() { + self.waker.register(waker); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + } +} + impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> where Self: 'd, @@ -299,26 +370,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.buf.is_full() { - do_pend = true; - } - state.rx.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx.waker.register(cx.waker()); - Poll::Pending + state.rx.read(buf, cx.waker()) }); if do_pend { @@ -337,26 +391,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.buf.is_full() { - do_pend = true; - } - state.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.waker.register(cx.waker()); - Poll::Pending + state.read(buf, cx.waker()) }); if do_pend { @@ -377,28 +414,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.rx.waker.register(cx.waker()); - Poll::>::Pending + state.rx.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.buf.is_full(); - state.rx.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.rx.consume(amt)); if signal { self.inner.pend(); } @@ -414,28 +436,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.waker.register(cx.waker()); - Poll::>::Pending + state.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.buf.is_full(); - state.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.consume(amt)); if signal { self.inner.pend(); } @@ -449,20 +456,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.buf.is_empty(); - let tx_buf = state.tx.buf.push_buf(); - if tx_buf.is_empty() { - state.tx.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -475,16 +469,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.buf.is_empty() { - state.tx.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) } } @@ -495,20 +480,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.buf.is_empty(); - let tx_buf = state.buf.push_buf(); - if tx_buf.is_empty() { - state.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -521,15 +493,6 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.buf.is_empty() { - state.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) } } From f76444bdc43f0a000b878dc52cb46ac06661a8b3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 05:32:45 +0200 Subject: [PATCH 47/62] Add HIL test for bufferedUart --- embassy-rp/src/uart/buffered.rs | 3 +-- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/uart_buffered.rs | 37 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/rp/src/bin/uart_buffered.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6d395b6f4..9c4fbfeb2 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; use super::*; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 7e2717ddf..503373759 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -20,6 +20,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +embedded-io = { version = "0.3.0", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs new file mode 100644 index 000000000..a0a3df8da --- /dev/null +++ b/tests/rp/src/bin/uart_buffered.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let config = Config::default(); + let uart = Uart::new_blocking(uart, tx, rx, config); + + let irq = interrupt::take!(UART0_IRQ); + let tx_buf = &mut [0u8; 32]; + let rx_buf = &mut [0u8; 32]; + let mut state = State::new(); + let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); + + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); + + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From c863acd24f6430950a7fdb5c527b33b42c305fec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 20:36:06 +0200 Subject: [PATCH 48/62] rp: set correct teleprobe target for rpi-pico tests. --- tests/rp/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index 0330025e4..9611db3a0 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -3,7 +3,7 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +#runner = "teleprobe client run --target rpi-pico --elf" runner = "teleprobe local run --chip RP2040 --elf" rustflags = [ From 5bf6564e95be4fa4240a08a4f524e9cf4bfee2bb Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 26 Sep 2022 20:33:24 +0200 Subject: [PATCH 49/62] Use firmware writer in stm32{f7, h7} example app The new FirmwareWriter is useful in particular for these architectures due to the large erase sector size. --- examples/boot/application/stm32f7/src/bin/a.rs | 17 +++++++++-------- examples/boot/application/stm32h7/src/bin/a.rs | 17 ++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index c08880fb3..77b897b0f 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -5,7 +5,6 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -17,8 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let mut flash = Flash::unlock(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -27,16 +25,19 @@ async fn main(_spawner: Spawner) { led.set_high(); let mut updater = FirmwareUpdater::default(); + let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; - let mut buf: [u8; 256 * 1024] = [0; 256 * 1024]; - for chunk in APP_B.chunks(256 * 1024) { - buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + let mut buf = AlignedBuffer([0; 4096]); + for chunk in APP_B.chunks(4096) { + buf.as_mut()[..chunk.len()].copy_from_slice(chunk); + writer + .write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len()) + .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index f5a8fdb61..0fe598a5d 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -5,7 +5,6 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -17,8 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let mut flash = Flash::unlock(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -27,19 +25,20 @@ async fn main(_spawner: Spawner) { led.set_high(); let mut updater = FirmwareUpdater::default(); + + let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; - let mut buf = AlignedBuffer([0; 128 * 1024]); - for chunk in APP_B.chunks(128 * 1024) { + let mut buf = AlignedBuffer([0; 4096]); + for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); - updater - .write_firmware(offset, buf.as_ref(), &mut flash, 2048) - .await + writer + .write_block_blocking(offset, buf.as_ref(), &mut flash, 4096) .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } From 65907204d64f104bfa5bf1ea9d1da8cb9cc09d19 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 05:51:31 +0200 Subject: [PATCH 50/62] Rename from {Rx,Tx}BufferedUart to BufferedUart{Rx,Tx} to be compliant with stm32 and nrf implementations --- embassy-rp/src/uart/buffered.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9c4fbfeb2..81ac61ee4 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -61,17 +61,17 @@ pub struct BufferedUart<'d, T: Instance> { inner: PeripheralMutex<'d, FullStateInner<'d, T>>, } -pub struct RxBufferedUart<'d, T: Instance> { +pub struct BufferedUartRx<'d, T: Instance> { inner: PeripheralMutex<'d, RxStateInner<'d, T>>, } -pub struct TxBufferedUart<'d, T: Instance> { +pub struct BufferedUartTx<'d, T: Instance> { inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} -impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} -impl<'d, T: Instance> Unpin for TxBufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for BufferedUartRx<'d, T> {} +impl<'d, T: Instance> Unpin for BufferedUartTx<'d, T> {} impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( @@ -109,13 +109,13 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } } -impl<'d, T: Instance> RxBufferedUart<'d, T> { +impl<'d, T: Instance> BufferedUartRx<'d, T> { pub fn new( state: &'d mut RxState<'d, T>, _uart: UartRx<'d, T, M>, irq: impl Peripheral

+ 'd, rx_buffer: &'d mut [u8], - ) -> RxBufferedUart<'d, T> { + ) -> BufferedUartRx<'d, T> { into_ref!(irq); let r = T::regs(); @@ -137,13 +137,13 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { } } -impl<'d, T: Instance> TxBufferedUart<'d, T> { +impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn new( state: &'d mut TxState<'d, T>, _uart: UartTx<'d, T, M>, irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], - ) -> TxBufferedUart<'d, T> { + ) -> BufferedUartTx<'d, T> { into_ref!(irq); let r = T::regs(); @@ -354,11 +354,11 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { +impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { +impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { type Error = Error; } @@ -383,7 +383,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { type ReadFuture<'a> = impl Future> where Self: 'a; @@ -426,7 +426,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { type FillBufFuture<'a> = impl Future> where Self: 'a; @@ -472,7 +472,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { type WriteFuture<'a> = impl Future> where Self: 'a; From 93354b812c89c1b0d56f93181eae8484c625fe89 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 05:51:38 +0200 Subject: [PATCH 51/62] Extend buffered-uart test to transmit 32 bytes --- tests/rp/src/bin/uart_buffered.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index a0a3df8da..4313ee3dd 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -25,10 +25,16 @@ async fn main(_spawner: Spawner) { let mut state = State::new(); let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); - let data = [0xC0, 0xDE]; + // Make sure we send more bytes than fits in the FIFO, to test the actual + // bufferedUart. + + let data = [ + 1_u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, + ]; uart.write(&data).await.unwrap(); - let mut buf = [0; 2]; + let mut buf = [0; 32]; uart.read(&mut buf).await.unwrap(); assert_eq!(buf, data); From e129a97d48a00d7923886ab3faa82357b2369f13 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 07:45:10 +0200 Subject: [PATCH 52/62] Fix bufferedUart read and write tests --- embassy-rp/src/uart/buffered.rs | 88 ++++++++++++++----------------- embassy-rp/src/uart/mod.rs | 7 ++- tests/rp/src/bin/uart_buffered.rs | 9 ++-- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 81ac61ee4..87e16f0eb 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -228,39 +228,39 @@ where fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartmis().read(); + let ris = r.uartris().read(); // Clear interrupt flags r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxmis() { - if ris.pemis() { - warn!("Parity error"); - r.uarticr().modify(|w| { - w.set_peic(true); - }); - } - if ris.femis() { - warn!("Framing error"); - r.uarticr().modify(|w| { - w.set_feic(true); - }); - } - if ris.bemis() { - warn!("Break error"); - r.uarticr().modify(|w| { - w.set_beic(true); - }); - } - if ris.oemis() { - warn!("Overrun error"); - r.uarticr().modify(|w| { - w.set_oeic(true); - }); - } + if ris.peris() { + warn!("Parity error"); + r.uarticr().modify(|w| { + w.set_peic(true); + }); + } + if ris.feris() { + warn!("Framing error"); + r.uarticr().modify(|w| { + w.set_feic(true); + }); + } + if ris.beris() { + warn!("Break error"); + r.uarticr().modify(|w| { + w.set_beic(true); + }); + } + if ris.oeris() { + warn!("Overrun error"); + r.uarticr().modify(|w| { + w.set_oeic(true); + }); + } + if !r.uartfr().read().rxfe() { let buf = self.buf.push_buf(); if !buf.is_empty() { buf[0] = r.uartdr().read().data(); @@ -274,7 +274,7 @@ where } } - if ris.rtmis() { + if ris.rtris() { self.waker.wake(); }; } @@ -318,27 +318,19 @@ where fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartris().read(); - // Clear interrupt flags - r.uarticr().write(|w| { - w.set_rtic(true); - }); - - if ris.txris() { - let buf = self.buf.pop_buf(); - if !buf.is_empty() { - r.uartimsc().modify(|w| { - w.set_txim(true); - }); - r.uartdr().write(|w| w.set_data(buf[0].into())); - self.buf.pop(1); - self.waker.wake(); - } else { - // Disable interrupt until we have something to transmit again - r.uartimsc().modify(|w| { - w.set_txim(false); - }); - } + let buf = self.buf.pop_buf(); + if !buf.is_empty() { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + r.uartdr().write(|w| w.set_data(buf[0].into())); + self.buf.pop(1); + self.waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); } } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 76ecdf7ac..d9285ee51 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,12 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(false); + w.set_fen(true); + }); + + r.uartifls().write(|w| { + w.set_rxiflsel(0b000); + w.set_txiflsel(0b000); }); r.uartcr().write(|w| { diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 4313ee3dd..9cc20bb98 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -20,8 +20,8 @@ async fn main(_spawner: Spawner) { let uart = Uart::new_blocking(uart, tx, rx, config); let irq = interrupt::take!(UART0_IRQ); - let tx_buf = &mut [0u8; 32]; - let rx_buf = &mut [0u8; 32]; + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; let mut state = State::new(); let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); @@ -32,10 +32,11 @@ async fn main(_spawner: Spawner) { 1_u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ]; - uart.write(&data).await.unwrap(); + uart.write_all(&data).await.unwrap(); + info!("Done writing"); let mut buf = [0; 32]; - uart.read(&mut buf).await.unwrap(); + uart.read_exact(&mut buf).await.unwrap(); assert_eq!(buf, data); info!("Test OK"); From 17d8d11f734fb0d48d5f19fd17f91c9f9fa62e57 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Sep 2022 13:09:56 +0200 Subject: [PATCH 53/62] usb: make HALs depend only on embassy-usb-driver. --- embassy-nrf/Cargo.toml | 6 +++--- embassy-nrf/src/usb.rs | 6 ++---- embassy-rp/Cargo.toml | 6 +++--- embassy-rp/src/usb.rs | 5 +++-- embassy-stm32/Cargo.toml | 6 +++--- embassy-stm32/src/usb/usb.rs | 5 +++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 6fbbc8d9b..d80281fa3 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -18,10 +18,10 @@ flavors = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] +nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. @@ -70,7 +70,7 @@ embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 20510eb49..00da5c9dd 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -9,10 +9,8 @@ use core::task::Poll; use cortex_m::peripheral::NVIC; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -pub use embassy_usb; -use embassy_usb::driver::{ - self, Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, -}; +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}; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index d0cf8025c..337a84f4a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -12,7 +12,7 @@ flavors = [ ] [features] -defmt = ["dep:defmt", "embassy-usb?/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/defmt"] # Reexport the PAC for the currently enabled chip at `embassy_rp::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version. @@ -27,7 +27,7 @@ intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -41,7 +41,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index ce473b21d..0a904aab3 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -7,8 +7,9 @@ use core::task::Poll; use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb::driver::{ - self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +use embassy_usb_driver as driver; +use embassy_usb_driver::{ + Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 484496f24..a4a232f51 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -39,7 +39,7 @@ embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} @@ -73,7 +73,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] @@ -92,7 +92,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 39809a3e1..2654f156a 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -9,8 +9,9 @@ use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; -use embassy_usb::driver::{ - self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +use embassy_usb_driver as driver; +use embassy_usb_driver::{ + Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; From 820e6462b6a48a6a59f082bccd5d7a3035937b2f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 11:51:42 +0200 Subject: [PATCH 54/62] Add preliminary I2C implementation for RP2040 --- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/i2c.rs | 221 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 4 + embassy-rp/src/uart/mod.rs | 5 + 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/i2c.rs diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1c446f389..875c129c0 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -114,7 +114,7 @@ pub unsafe fn init() { reset::unreset_wait(peris); } -pub(crate) fn _clk_sys_freq() -> u32 { +pub(crate) fn clk_sys_freq() -> u32 { 125_000_000 } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs new file mode 100644 index 000000000..4a27ee8df --- /dev/null +++ b/embassy-rp/src/i2c.rs @@ -0,0 +1,221 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use pac::i2c; + +use crate::{pac, peripherals, Peripheral}; + +/// I2C error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// I2C abort with error + Abort(u32), + /// User passed in a read buffer that was 0 length + InvalidReadBufferLength, + /// User passed in a write buffer that was 0 length + InvalidWriteBufferLength, + /// Target i2c address is out of range + AddressOutOfRange(u16), + /// Target i2c address is reserved + AddressReserved(u16), +} + +#[non_exhaustive] +#[derive(Copy, Clone)] +pub struct Config { + pub frequency: u32, + pub sda_pullup: bool, + pub scl_pullup: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + frequency: 100_000, + sda_pullup: false, + scl_pullup: false, + } + } +} + +pub struct I2c<'d, T: Instance, M: Mode> { + phantom: PhantomData<(&'d mut T, M)>, +} + +impl<'d, T: Instance> I2c<'d, T, Master> { + pub fn new_master( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(_peri, scl, sda); + + assert!(config.frequency <= 1_000_000); + assert!(config.frequency > 0); + + let p = T::regs(); + + unsafe { + p.ic_enable().write(|w| w.set_enable(false)); + + // select controller mode & speed + p.ic_con().write(|w| { + // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) + w.set_speed(i2c::vals::Speed::FAST); + w.set_master_mode(true); + w.set_ic_slave_disable(true); + w.set_ic_restart_en(true); + w.set_tx_empty_ctrl(true); + }); + + // Clear FIFO threshold + p.ic_tx_tl().write(|w| w.set_tx_tl(0)); + p.ic_rx_tl().write(|w| w.set_rx_tl(0)); + + // Configure SCL & SDA pins + scl.io().ctrl().write(|w| w.set_funcsel(3)); + sda.io().ctrl().write(|w| w.set_funcsel(3)); + + scl.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_pue(config.scl_pullup); + }); + sda.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_pue(config.sda_pullup); + }); + + // Configure baudrate + + // There are some subtleties to I2C timing which we are completely ignoring here + // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 + let clk_base = crate::clocks::clk_sys_freq(); + + let period = (clk_base + config.frequency / 2) / config.frequency; + let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low + let hcnt = period - lcnt; // and 2/5 (40%) of the period high + + // Check for out-of-range divisors: + assert!(hcnt <= 0xffff); + assert!(lcnt <= 0xffff); + assert!(hcnt >= 8); + assert!(lcnt >= 8); + + // Per I2C-bus specification a device in standard or fast mode must + // internally provide a hold time of at least 300ns for the SDA signal to + // bridge the undefined region of the falling edge of SCL. A smaller hold + // time of 120ns is used for fast mode plus. + let sda_tx_hold_count = if config.frequency < 1_000_000 { + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) + // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. + // Add 1 to avoid division truncation. + ((clk_base * 3) / 10_000_000) + 1 + } else { + // fast mode plus requires a clk_base > 32MHz + assert!(clk_base >= 32_000_000); + + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) + // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. + // Add 1 to avoid division truncation. + ((clk_base * 3) / 25_000_000) + 1 + }; + assert!(sda_tx_hold_count <= lcnt - 2); + + p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); + p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); + p.ic_fs_spklen() + .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); + p.ic_sda_hold() + .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); + + // Enable I2C block + p.ic_enable().write(|w| w.set_enable(true)); + } + + Self { phantom: PhantomData } + } +} + +mod sealed { + pub trait Instance {} + pub trait Mode {} + + pub trait SdaPin {} + pub trait SclPin {} +} + +pub trait Mode: sealed::Mode {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Master; +pub struct Slave; + +impl_mode!(Master); +impl_mode!(Slave); + +pub trait Instance: sealed::Instance { + fn regs() -> pac::i2c::I2c; +} + +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type {} + impl Instance for peripherals::$type { + fn regs() -> pac::i2c::I2c { + pac::$type + } + } + }; +} + +impl_instance!(I2C0, I2c0); +impl_instance!(I2C1, I2c1); + +pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} +pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} + +macro_rules! impl_pin { + ($pin:ident, $instance:ident, $function:ident) => { + impl sealed::$function for peripherals::$pin {} + impl $function for peripherals::$pin {} + }; +} + +impl_pin!(PIN_0, I2C0, SdaPin); +impl_pin!(PIN_1, I2C0, SclPin); +impl_pin!(PIN_2, I2C1, SdaPin); +impl_pin!(PIN_3, I2C1, SclPin); +impl_pin!(PIN_4, I2C0, SdaPin); +impl_pin!(PIN_5, I2C0, SclPin); +impl_pin!(PIN_6, I2C1, SdaPin); +impl_pin!(PIN_7, I2C1, SclPin); +impl_pin!(PIN_8, I2C0, SdaPin); +impl_pin!(PIN_9, I2C0, SclPin); +impl_pin!(PIN_10, I2C1, SdaPin); +impl_pin!(PIN_11, I2C1, SclPin); +impl_pin!(PIN_12, I2C0, SdaPin); +impl_pin!(PIN_13, I2C0, SclPin); +impl_pin!(PIN_14, I2C1, SdaPin); +impl_pin!(PIN_15, I2C1, SclPin); +impl_pin!(PIN_16, I2C0, SdaPin); +impl_pin!(PIN_17, I2C0, SclPin); +impl_pin!(PIN_18, I2C1, SdaPin); +impl_pin!(PIN_19, I2C1, SclPin); +impl_pin!(PIN_20, I2C0, SdaPin); +impl_pin!(PIN_21, I2C0, SclPin); +impl_pin!(PIN_22, I2C1, SdaPin); +impl_pin!(PIN_23, I2C1, SclPin); +impl_pin!(PIN_24, I2C0, SdaPin); +impl_pin!(PIN_25, I2C0, SclPin); +impl_pin!(PIN_26, I2C1, SdaPin); +impl_pin!(PIN_27, I2C1, SclPin); +impl_pin!(PIN_28, I2C0, SdaPin); +impl_pin!(PIN_29, I2C0, SclPin); diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ac98d226..e784399d4 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ mod intrinsics; pub mod dma; pub mod gpio; +pub mod i2c; pub mod interrupt; pub mod rom_data; pub mod rtc; @@ -75,6 +76,9 @@ embassy_hal_common::peripherals! { SPI0, SPI1, + I2C0, + I2C1, + DMA_CH0, DMA_CH1, DMA_CH2, diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index d9285ee51..567c79db3 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -428,9 +428,11 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } + fn bflush(&mut self) -> Result<(), Self::Error> { self.blocking_flush() } @@ -438,6 +440,7 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { type Error = Error; + fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } @@ -445,9 +448,11 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } + fn bflush(&mut self) -> Result<(), Self::Error> { self.blocking_flush() } From bcd3ab4ba1541a098a74b5abffdb30a293c97f64 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 14:15:43 +0200 Subject: [PATCH 55/62] Add blocking read & write for I2C --- embassy-rp/src/i2c.rs | 306 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 286 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 4a27ee8df..f7e6a6f6b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -39,12 +39,15 @@ impl Default for Config { } } +const TX_FIFO_SIZE: u8 = 16; +const RX_FIFO_SIZE: u8 = 16; + pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Master> { - pub fn new_master( +impl<'d, T: Instance> I2c<'d, T, Blocking> { + pub fn new_blocking( _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, @@ -60,9 +63,10 @@ impl<'d, T: Instance> I2c<'d, T, Master> { unsafe { p.ic_enable().write(|w| w.set_enable(false)); - // select controller mode & speed + // Select controller mode & speed p.ic_con().write(|w| { - // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) + // Always use "fast" mode (<= 400 kHz, works fine for standard + // mode too) w.set_speed(i2c::vals::Speed::FAST); w.set_master_mode(true); w.set_ic_slave_disable(true); @@ -70,7 +74,8 @@ impl<'d, T: Instance> I2c<'d, T, Master> { w.set_tx_empty_ctrl(true); }); - // Clear FIFO threshold + // Set FIFO watermarks to 1 to make things simpler. This is encoded + // by a register value of 0. p.ic_tx_tl().write(|w| w.set_tx_tl(0)); p.ic_rx_tl().write(|w| w.set_rx_tl(0)); @@ -89,8 +94,9 @@ impl<'d, T: Instance> I2c<'d, T, Master> { // Configure baudrate - // There are some subtleties to I2C timing which we are completely ignoring here - // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 + // There are some subtleties to I2C timing which we are completely + // ignoring here See: + // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 let clk_base = crate::clocks::clk_sys_freq(); let period = (clk_base + config.frequency / 2) / config.frequency; @@ -104,21 +110,21 @@ impl<'d, T: Instance> I2c<'d, T, Master> { assert!(lcnt >= 8); // Per I2C-bus specification a device in standard or fast mode must - // internally provide a hold time of at least 300ns for the SDA signal to - // bridge the undefined region of the falling edge of SCL. A smaller hold - // time of 120ns is used for fast mode plus. + // internally provide a hold time of at least 300ns for the SDA + // signal to bridge the undefined region of the falling edge of SCL. + // A smaller hold time of 120ns is used for fast mode plus. let sda_tx_hold_count = if config.frequency < 1_000_000 { - // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) - // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. - // Add 1 to avoid division truncation. + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / + // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. ((clk_base * 3) / 10_000_000) + 1 } else { // fast mode plus requires a clk_base > 32MHz assert!(clk_base >= 32_000_000); - // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) - // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. - // Add 1 to avoid division truncation. + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / + // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. ((clk_base * 3) / 25_000_000) + 1 }; assert!(sda_tx_hold_count <= lcnt - 2); @@ -138,6 +144,266 @@ impl<'d, T: Instance> I2c<'d, T, Master> { } } +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + /// Number of bytes currently in the RX FIFO + #[inline] + pub fn rx_fifo_used(&self) -> u8 { + unsafe { T::regs().ic_rxflr().read().rxflr() } + } + + /// Remaining capacity in the RX FIFO + #[inline] + pub fn rx_fifo_free(&self) -> u8 { + RX_FIFO_SIZE - self.rx_fifo_used() + } + + /// RX FIFO is empty + #[inline] + pub fn rx_fifo_empty(&self) -> bool { + self.rx_fifo_used() == 0 + } + + /// Number of bytes currently in the TX FIFO + #[inline] + pub fn tx_fifo_used(&self) -> u8 { + unsafe { T::regs().ic_txflr().read().txflr() } + } + + /// Remaining capacity in the TX FIFO + #[inline] + pub fn tx_fifo_free(&self) -> u8 { + TX_FIFO_SIZE - self.tx_fifo_used() + } + + /// TX FIFO is at capacity + #[inline] + pub fn tx_fifo_full(&self) -> bool { + self.tx_fifo_free() == 0 + } + + fn setup(addr: u16) -> Result<(), Error> { + if addr >= 0x80 { + return Err(Error::AddressOutOfRange(addr)); + } + + if i2c_reserved_addr(addr) { + return Err(Error::AddressReserved(addr)); + } + + let p = T::regs(); + unsafe { + p.ic_enable().write(|w| w.set_enable(false)); + p.ic_tar().write(|w| w.set_ic_tar(addr)); + p.ic_enable().write(|w| w.set_enable(true)); + } + Ok(()) + } + + fn read_and_clear_abort_reason(&mut self) -> Option { + let p = T::regs(); + unsafe { + let abort_reason = p.ic_tx_abrt_source().read().0; + if abort_reason != 0 { + // Note clearing the abort flag also clears the reason, and this + // instance of flag is clear-on-read! Note also the + // IC_CLR_TX_ABRT register always reads as 0. + p.ic_clr_tx_abrt().read(); + Some(abort_reason) + } else { + None + } + } + } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // NOTE(unsafe) We have &mut self + unsafe { + // wait until there is space in the FIFO to write the next byte + while self.tx_fifo_full() {} + + p.ic_data_cmd().write(|w| { + if restart && first { + w.set_restart(true); + } else { + w.set_restart(false); + } + + if send_stop && last { + w.set_stop(true); + } else { + w.set_stop(false); + } + + w.cmd() + }); + + while p.ic_rxflr().read().rxflr() == 0 { + if let Some(abort_reason) = self.read_and_clear_abort_reason() { + return Err(Error::Abort(abort_reason)); + } + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + if send_stop && last { + w.set_stop(true); + } else { + w.set_stop(false); + } + w.set_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_some() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + if let Some(abort_reason) = abort_reason { + return Err(Error::Abort(abort_reason)); + } + } + } + Ok(()) + } + + // ========================= Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(buffer, false, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } +} + +// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // +// Async public API // ========================= + +// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), +// Error> { if bytes.is_empty() { self.write_blocking_internal(address, +// bytes, true) } else { self.write_dma_internal(address, bytes, +// true, true).await } } + +// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> +// Result<(), Error> { if bytes.is_empty() { return +// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); + +// let mut first = true; let mut current = iter.next(); while let +// Some(c) = current { let next = iter.next(); let is_last = +// next.is_none(); + +// self.write_dma_internal(address, c, first, is_last).await?; +// first = false; +// current = next; +// } Ok(()) +// } + +// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> +// Result<(), Error> { if buffer.is_empty() { +// self.read_blocking_internal(address, buffer, false) } else { +// self.read_dma_internal(address, buffer, false).await } } + +// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: +// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { +// self.write_blocking_internal(address, bytes, false)?; } else { +// self.write_dma_internal(address, bytes, true, true).await?; } + +// if buffer.is_empty() { self.read_blocking_internal(address, buffer, +// true)?; } else { self.read_dma_internal(address, buffer, +// true).await?; } + +// Ok(()) +// } +// } + +mod eh02 { + use super::*; + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { + type Error = Error; + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, bytes) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { + type Error = Error; + + fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, bytes, buffer) + } + } +} + +fn i2c_reserved_addr(addr: u16) -> bool { + (addr & 0x78) == 0 || (addr & 0x78) == 0x78 +} + mod sealed { pub trait Instance {} pub trait Mode {} @@ -155,11 +421,11 @@ macro_rules! impl_mode { }; } -pub struct Master; -pub struct Slave; +pub struct Blocking; +pub struct Async; -impl_mode!(Master); -impl_mode!(Slave); +impl_mode!(Blocking); +impl_mode!(Async); pub trait Instance: sealed::Instance { fn regs() -> pac::i2c::I2c; From 603513e76e0cf727808033540598c6c7dd597133 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:01:33 +0200 Subject: [PATCH 56/62] Fix blocking I2C --- embassy-rp/src/i2c.rs | 196 +++++++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 69 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f7e6a6f6b..b368c49cf 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -25,22 +25,17 @@ pub enum Error { #[derive(Copy, Clone)] pub struct Config { pub frequency: u32, - pub sda_pullup: bool, - pub scl_pullup: bool, } impl Default for Config { fn default() -> Self { Self { frequency: 100_000, - sda_pullup: false, - scl_pullup: false, } } } -const TX_FIFO_SIZE: u8 = 16; -const RX_FIFO_SIZE: u8 = 16; +const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, @@ -64,7 +59,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(false)); // Select controller mode & speed - p.ic_con().write(|w| { + p.ic_con().modify(|w| { // Always use "fast" mode (<= 400 kHz, works fine for standard // mode too) w.set_speed(i2c::vals::Speed::FAST); @@ -85,11 +80,17 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { scl.pad_ctrl().write(|w| { w.set_schmitt(true); - w.set_pue(config.scl_pullup); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); }); sda.pad_ctrl().write(|w| { w.set_schmitt(true); - w.set_pue(config.sda_pullup); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); }); // Configure baudrate @@ -97,7 +98,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // There are some subtleties to I2C timing which we are completely // ignoring here See: // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 - let clk_base = crate::clocks::clk_sys_freq(); + let clk_base = crate::clocks::clk_peri_freq(); let period = (clk_base + config.frequency / 2) / config.frequency; let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low @@ -134,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_fs_spklen() .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); p.ic_sda_hold() - .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); + .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); // Enable I2C block p.ic_enable().write(|w| w.set_enable(true)); @@ -145,42 +146,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { - /// Number of bytes currently in the RX FIFO - #[inline] - pub fn rx_fifo_used(&self) -> u8 { - unsafe { T::regs().ic_rxflr().read().rxflr() } - } - - /// Remaining capacity in the RX FIFO - #[inline] - pub fn rx_fifo_free(&self) -> u8 { - RX_FIFO_SIZE - self.rx_fifo_used() - } - - /// RX FIFO is empty - #[inline] - pub fn rx_fifo_empty(&self) -> bool { - self.rx_fifo_used() == 0 - } - - /// Number of bytes currently in the TX FIFO - #[inline] - pub fn tx_fifo_used(&self) -> u8 { - unsafe { T::regs().ic_txflr().read().txflr() } - } - - /// Remaining capacity in the TX FIFO - #[inline] - pub fn tx_fifo_free(&self) -> u8 { - TX_FIFO_SIZE - self.tx_fifo_used() - } - - /// TX FIFO is at capacity - #[inline] - pub fn tx_fifo_full(&self) -> bool { - self.tx_fifo_free() == 0 - } - fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -229,22 +194,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // NOTE(unsafe) We have &mut self unsafe { // wait until there is space in the FIFO to write the next byte - while self.tx_fifo_full() {} + while p.ic_txflr().read().txflr() == FIFO_SIZE {} p.ic_data_cmd().write(|w| { - if restart && first { - w.set_restart(true); - } else { - w.set_restart(false); - } + w.set_restart(restart && first); + w.set_stop(send_stop && last); - if send_stop && last { - w.set_stop(true); - } else { - w.set_stop(false); - } - - w.cmd() + w.set_cmd(true); }); while p.ic_rxflr().read().rxflr() == 0 { @@ -273,11 +229,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // NOTE(unsafe) We have &mut self unsafe { p.ic_data_cmd().write(|w| { - if send_stop && last { - w.set_stop(true); - } else { - w.set_stop(false); - } + w.set_stop(send_stop && last); w.set_dat(*byte); }); @@ -310,12 +262,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - // ========================= Blocking public API + // ========================= + // Blocking public API // ========================= pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.read_blocking_internal(buffer, false, true) + self.read_blocking_internal(buffer, true, true) // Automatic Stop } @@ -400,6 +353,107 @@ mod eh02 { } } +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl embedded_hal_1::i2c::Error for Error { + fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { + match *self { + _ => embedded_hal_1::i2c::ErrorKind::Bus, + // Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + // Self::Nack => { + // embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) + // } + // Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, + // Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, + // Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, + // Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { + type Error = Error; + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> { + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + + fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, buffer) + } + + fn write_iter(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let mut peekable = bytes.into_iter().peekable(); + Self::setup(address.into())?; + + while let Some(tx) = peekable.next() { + self.write_blocking_internal(&[tx], peekable.peek().is_none())?; + } + Ok(()) + } + + fn write_iter_read(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let peekable = bytes.into_iter().peekable(); + Self::setup(address.into())?; + + for tx in peekable { + self.write_blocking_internal(&[tx], false)? + } + self.read_blocking_internal(buffer, true, true) + } + + fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, wr_buffer, rd_buffer) + } + + fn transaction<'a>( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + ) -> Result<(), Self::Error> { + Self::setup(address.into())?; + for i in 0..operations.len() { + let last = i == operations.len() - 1; + match &mut operations[i] { + embedded_hal_1::i2c::blocking::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + + fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + Self::setup(address.into())?; + let mut peekable = operations.into_iter().peekable(); + while let Some(operation) = peekable.next() { + let last = peekable.peek().is_none(); + match operation { + embedded_hal_1::i2c::blocking::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + } +} + fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 } @@ -428,6 +482,8 @@ impl_mode!(Blocking); impl_mode!(Async); pub trait Instance: sealed::Instance { + type Interrupt; + fn regs() -> pac::i2c::I2c; } @@ -435,6 +491,8 @@ macro_rules! impl_instance { ($type:ident, $irq:ident) => { impl sealed::Instance for peripherals::$type {} impl Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + fn regs() -> pac::i2c::I2c { pac::$type } @@ -442,8 +500,8 @@ macro_rules! impl_instance { }; } -impl_instance!(I2C0, I2c0); -impl_instance!(I2C1, I2c1); +impl_instance!(I2C0, I2C0_IRQ); +impl_instance!(I2C1, I2C1_IRQ); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From be68d8ebb773fcf6d0cb94f3bc580d6661f6779b Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 14:24:49 +0200 Subject: [PATCH 57/62] Add further i2c error types --- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/i2c.rs | 69 +++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 875c129c0..1c446f389 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -114,7 +114,7 @@ pub unsafe fn init() { reset::unreset_wait(peris); } -pub(crate) fn clk_sys_freq() -> u32 { +pub(crate) fn _clk_sys_freq() -> u32 { 125_000_000 } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index b368c49cf..20616cd62 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -5,12 +5,25 @@ use pac::i2c; use crate::{pac, peripherals, Peripheral}; +/// I2C error abort reason +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AbortReason { + /// A bus operation was not acknowledged, e.g. due to the addressed device + /// not being available on the bus or the device not being ready to process + /// requests at the moment + NoAcknowledge, + /// The arbitration was lost, e.g. electrical problems with the clock signal + ArbitrationLoss, + Other(u32), +} + /// I2C error #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// I2C abort with error - Abort(u32), + Abort(AbortReason), /// User passed in a read buffer that was 0 length InvalidReadBufferLength, /// User passed in a write buffer that was 0 length @@ -29,9 +42,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Self { - frequency: 100_000, - } + Self { frequency: 100_000 } } } @@ -164,18 +175,30 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - fn read_and_clear_abort_reason(&mut self) -> Option { + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); unsafe { - let abort_reason = p.ic_tx_abrt_source().read().0; - if abort_reason != 0 { + let abort_reason = p.ic_tx_abrt_source().read(); + if abort_reason.0 != 0 { // Note clearing the abort flag also clears the reason, and this // instance of flag is clear-on-read! Note also the // IC_CLR_TX_ABRT register always reads as 0. p.ic_clr_tx_abrt().read(); - Some(abort_reason) + + let reason = if abort_reason.abrt_7b_addr_noack() + | abort_reason.abrt_10addr1_noack() + | abort_reason.abrt_10addr2_noack() + { + AbortReason::NoAcknowledge + } else if abort_reason.arb_lost() { + AbortReason::ArbitrationLoss + } else { + AbortReason::Other(abort_reason.0) + }; + + Err(Error::Abort(reason)) } else { - None + Ok(()) } } } @@ -204,9 +227,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { }); while p.ic_rxflr().read().rxflr() == 0 { - if let Some(abort_reason) = self.read_and_clear_abort_reason() { - return Err(Error::Abort(abort_reason)); - } + self.read_and_clear_abort_reason()?; } *byte = p.ic_data_cmd().read().dat(); @@ -241,7 +262,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let abort_reason = self.read_and_clear_abort_reason(); - if abort_reason.is_some() || (send_stop && last) { + if abort_reason.is_err() || (send_stop && last) { // If the transaction was aborted or if it completed // successfully wait until the STOP condition has occured. @@ -254,9 +275,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // condition. Note also the hardware clears RX FIFO as well as // TX on abort, ecause we set hwparam // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - if let Some(abort_reason) = abort_reason { - return Err(Error::Abort(abort_reason)); - } + abort_reason?; } } Ok(()) @@ -360,15 +379,15 @@ mod eh1 { impl embedded_hal_1::i2c::Error for Error { fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { match *self { - _ => embedded_hal_1::i2c::ErrorKind::Bus, - // Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, - // Self::Nack => { - // embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) - // } - // Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, - // Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, - // Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, - // Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, + Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + Self::Abort(AbortReason::NoAcknowledge) => { + embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) + } + Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, + Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, + Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, + Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other, + Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other, } } } From 53c34ccc399c7bec98880657e52842477af09f49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 29 Aug 2022 13:31:17 +0200 Subject: [PATCH 58/62] Add async API for I2C --- embassy-rp/src/i2c.rs | 281 +++++++++++++++++++++++++++++++++--------- 1 file changed, 225 insertions(+), 56 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 20616cd62..19cdef133 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,8 +1,13 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use atomic_polyfill::Ordering; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; +use crate::dma::{AnyChannel, Channel}; +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; /// I2C error abort reason @@ -49,9 +54,165 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { + tx_dma: Option>, + rx_dma: Option>, + dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq, tx_dma, rx_dma); + + // Enable interrupts + unsafe { + T::regs().ic_intr_mask().modify(|w| { + w.set_m_rx_done(true); + }); + } + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self::new_inner( + _peri, + scl.map_into(), + sda.map_into(), + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), + config, + ) + } + + unsafe fn on_interrupt(_: *mut ()) { + let status = T::regs().ic_intr_stat().read(); + + // FIXME: + if status.tcr() || status.tc() { + let state = T::state(); + state.chunks_transferred.fetch_add(1, Ordering::Relaxed); + state.waker.wake(); + } + // The flag can only be cleared by writting to nbytes, we won't do that here, so disable + // the interrupt + // critical_section::with(|_| { + // regs.cr1().modify(|w| w.set_tcie(false)); + // }); + } + + async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + let len = bytes.len(); + for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { + let first = idx == 0; + let last = idx * self.dma_buf.len() + chunk.len() == len; + + for (i, byte) in chunk.iter().enumerate() { + let mut b = i2c::regs::IcDataCmd::default(); + b.set_dat(*byte); + b.set_stop(send_stop && last); + + self.dma_buf[i] = b.0 as u16; + } + + // Note(safety): Unwrap should be safe, as this can only be called + // when `Mode == Async`, where we have dma channels. + let ch = self.tx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().ic_dma_cr().modify(|w| { + w.set_tdmae(true); + }); + + crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) + }; + + transfer.await; + } + + Ok(()) + } + + async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let len = buffer.len(); + self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; + + if len > 2 { + // Note(safety): Unwrap should be safe, as this can only be called + // when `Mode == Async`, where we have dma channels. + let ch = self.rx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().ic_data_cmd().modify(|w| { + w.set_cmd(true); + }); + + T::regs().ic_dma_cr().modify(|reg| { + reg.set_rdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read( + ch, + T::regs().ic_data_cmd().ptr() as *const _, + &mut buffer[1..len - 1], + T::RX_DREQ, + ) + }; + transfer.await; + } + + if len > 2 { + self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; + } + + Ok(()) + } + + // ========================= + // Async public API + // ========================= + + pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if bytes.is_empty() { + self.write_blocking_internal(bytes, true) + } else { + self.write_internal(bytes, true).await + } + } + + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if buffer.is_empty() { + self.read_blocking_internal(buffer, true, true) + } else { + self.read_internal(buffer).await + } + } + + pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if bytes.is_empty() { + self.write_blocking_internal(bytes, false)?; + } else { + self.write_internal(bytes, false).await?; + } + + if buffer.is_empty() { + self.read_blocking_internal(buffer, true, true) + } else { + self.read_internal(buffer).await + } + } +} + impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( _peri: impl Peripheral

+ 'd, @@ -59,7 +220,21 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { sda: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(_peri, scl, sda); + into_ref!(scl, sda); + Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + scl: PeripheralRef<'d, AnyPin>, + sda: PeripheralRef<'d, AnyPin>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Self { + into_ref!(_peri); assert!(config.frequency <= 1_000_000); assert!(config.frequency > 0); @@ -152,11 +327,14 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { phantom: PhantomData } + Self { + tx_dma, + rx_dma, + dma_buf: [0; 256], + phantom: PhantomData, + } } -} -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -304,46 +482,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } -// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // -// Async public API // ========================= - -// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), -// Error> { if bytes.is_empty() { self.write_blocking_internal(address, -// bytes, true) } else { self.write_dma_internal(address, bytes, -// true, true).await } } - -// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> -// Result<(), Error> { if bytes.is_empty() { return -// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); - -// let mut first = true; let mut current = iter.next(); while let -// Some(c) = current { let next = iter.next(); let is_last = -// next.is_none(); - -// self.write_dma_internal(address, c, first, is_last).await?; -// first = false; -// current = next; -// } Ok(()) -// } - -// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> -// Result<(), Error> { if buffer.is_empty() { -// self.read_blocking_internal(address, buffer, false) } else { -// self.read_dma_internal(address, buffer, false).await } } - -// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: -// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { -// self.write_blocking_internal(address, bytes, false)?; } else { -// self.write_dma_internal(address, bytes, true, true).await?; } - -// if buffer.is_empty() { self.read_blocking_internal(address, buffer, -// true)?; } else { self.read_dma_internal(address, buffer, -// true).await?; } - -// Ok(()) -// } -// } - mod eh02 { use super::*; @@ -478,7 +616,34 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - pub trait Instance {} + use atomic_polyfill::AtomicUsize; + use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; + + pub(crate) struct State { + pub(crate) waker: AtomicWaker, + pub(crate) chunks_transferred: AtomicUsize, + } + + impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + chunks_transferred: AtomicUsize::new(0), + } + } + } + + pub trait Instance { + const TX_DREQ: u8; + const RX_DREQ: u8; + + type Interrupt: Interrupt; + + fn regs() -> crate::pac::i2c::I2c; + fn state() -> &'static State; + } + pub trait Mode {} pub trait SdaPin {} @@ -500,27 +665,31 @@ pub struct Async; impl_mode!(Blocking); impl_mode!(Async); -pub trait Instance: sealed::Instance { - type Interrupt; - - fn regs() -> pac::i2c::I2c; -} +pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident) => { - impl sealed::Instance for peripherals::$type {} - impl Instance for peripherals::$type { + ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + impl sealed::Instance for peripherals::$type { + const TX_DREQ: u8 = $tx_dreq; + const RX_DREQ: u8 = $rx_dreq; + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::i2c::I2c { pac::$type } + + fn state() -> &'static sealed::State { + static STATE: sealed::State = sealed::State::new(); + &STATE + } } + impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, I2C0_IRQ); -impl_instance!(I2C1, I2C1_IRQ); +impl_instance!(I2C0, I2C0_IRQ, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From b0d91e9f310f86b4eb9d75c92471831f1656ed1b Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Thu, 15 Sep 2022 05:38:55 +0200 Subject: [PATCH 59/62] Apply suggestions from code review Co-authored-by: Jacob Gonzalez --- embassy-rp/src/i2c.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 19cdef133..12fae3b7b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -144,7 +144,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let len = buffer.len(); self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; - if len > 2 { + if len >= 2 { // Note(safety): Unwrap should be safe, as this can only be called // when `Mode == Async`, where we have dma channels. let ch = self.rx_dma.as_mut().unwrap(); @@ -168,7 +168,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { transfer.await; } - if len > 2 { + if len >= 2 { self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; } @@ -202,7 +202,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if bytes.is_empty() { self.write_blocking_internal(bytes, false)?; } else { - self.write_internal(bytes, false).await?; + self.write_internal(bytes, true).await?; } if buffer.is_empty() { From 44c46e3c93ae0718110a8805cab4c6c8a9b5df55 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 07:55:28 +0200 Subject: [PATCH 60/62] Move async i2c implementation to new PR, to merge working blocking implementation faster --- embassy-rp/src/i2c.rs | 179 +----------------------------------------- 1 file changed, 1 insertion(+), 178 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 12fae3b7b..d1ec77d33 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,11 +1,9 @@ use core::marker::PhantomData; -use atomic_polyfill::Ordering; -use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; -use crate::dma::{AnyChannel, Channel}; +use crate::dma::AnyChannel; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -60,159 +58,6 @@ pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Async> { - pub fn new( - _peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(scl, sda, irq, tx_dma, rx_dma); - - // Enable interrupts - unsafe { - T::regs().ic_intr_mask().modify(|w| { - w.set_m_rx_done(true); - }); - } - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner( - _peri, - scl.map_into(), - sda.map_into(), - Some(tx_dma.map_into()), - Some(rx_dma.map_into()), - config, - ) - } - - unsafe fn on_interrupt(_: *mut ()) { - let status = T::regs().ic_intr_stat().read(); - - // FIXME: - if status.tcr() || status.tc() { - let state = T::state(); - state.chunks_transferred.fetch_add(1, Ordering::Relaxed); - state.waker.wake(); - } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt - // critical_section::with(|_| { - // regs.cr1().modify(|w| w.set_tcie(false)); - // }); - } - - async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - let len = bytes.len(); - for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { - let first = idx == 0; - let last = idx * self.dma_buf.len() + chunk.len() == len; - - for (i, byte) in chunk.iter().enumerate() { - let mut b = i2c::regs::IcDataCmd::default(); - b.set_dat(*byte); - b.set_stop(send_stop && last); - - self.dma_buf[i] = b.0 as u16; - } - - // Note(safety): Unwrap should be safe, as this can only be called - // when `Mode == Async`, where we have dma channels. - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - T::regs().ic_dma_cr().modify(|w| { - w.set_tdmae(true); - }); - - crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) - }; - - transfer.await; - } - - Ok(()) - } - - async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let len = buffer.len(); - self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; - - if len >= 2 { - // Note(safety): Unwrap should be safe, as this can only be called - // when `Mode == Async`, where we have dma channels. - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - T::regs().ic_data_cmd().modify(|w| { - w.set_cmd(true); - }); - - T::regs().ic_dma_cr().modify(|reg| { - reg.set_rdmae(true); - }); - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read( - ch, - T::regs().ic_data_cmd().ptr() as *const _, - &mut buffer[1..len - 1], - T::RX_DREQ, - ) - }; - transfer.await; - } - - if len >= 2 { - self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; - } - - Ok(()) - } - - // ========================= - // Async public API - // ========================= - - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if bytes.is_empty() { - self.write_blocking_internal(bytes, true) - } else { - self.write_internal(bytes, true).await - } - } - - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if buffer.is_empty() { - self.read_blocking_internal(buffer, true, true) - } else { - self.read_internal(buffer).await - } - } - - pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if bytes.is_empty() { - self.write_blocking_internal(bytes, false)?; - } else { - self.write_internal(bytes, true).await?; - } - - if buffer.is_empty() { - self.read_blocking_internal(buffer, true, true) - } else { - self.read_internal(buffer).await - } - } -} - impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( _peri: impl Peripheral

+ 'd, @@ -616,23 +461,7 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - use atomic_polyfill::AtomicUsize; use embassy_cortex_m::interrupt::Interrupt; - use embassy_sync::waitqueue::AtomicWaker; - - pub(crate) struct State { - pub(crate) waker: AtomicWaker, - pub(crate) chunks_transferred: AtomicUsize, - } - - impl State { - pub(crate) const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - chunks_transferred: AtomicUsize::new(0), - } - } - } pub trait Instance { const TX_DREQ: u8; @@ -641,7 +470,6 @@ mod sealed { type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; - fn state() -> &'static State; } pub trait Mode {} @@ -678,11 +506,6 @@ macro_rules! impl_instance { fn regs() -> pac::i2c::I2c { pac::$type } - - fn state() -> &'static sealed::State { - static STATE: sealed::State = sealed::State::new(); - &STATE - } } impl Instance for peripherals::$type {} }; From bf1da0497ce1a591865f2bd8d0a29489079e710b Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 08:03:49 +0200 Subject: [PATCH 61/62] Allow unused fields temporarily in i2c --- embassy-rp/src/i2c.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d1ec77d33..9596d661d 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -52,9 +52,9 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { - tx_dma: Option>, - rx_dma: Option>, - dma_buf: [u16; 256], + _tx_dma: Option>, + _rx_dma: Option>, + _dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } @@ -75,8 +75,8 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, sda: PeripheralRef<'d, AnyPin>, - tx_dma: Option>, - rx_dma: Option>, + _tx_dma: Option>, + _rx_dma: Option>, config: Config, ) -> Self { into_ref!(_peri); @@ -173,9 +173,9 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } Self { - tx_dma, - rx_dma, - dma_buf: [0; 256], + _tx_dma, + _rx_dma, + _dma_buf: [0; 256], phantom: PhantomData, } } From d7f7614b225da41477972b5fd287d10e6069846c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 28 Sep 2022 11:32:11 +0200 Subject: [PATCH 62/62] Remove subghz static lifetime requirement --- embassy-lora/src/stm32wl/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index e28fa2c1a..8d5d19531 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -234,15 +234,15 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf Ok(()) } -impl PhyRxTx for SubGhzRadio<'static, RS> { +impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm where RS: 'm; + type TxFuture<'m> = impl Future> + 'm where Self: 'm; fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { async move { self.do_tx(config, buf).await } } - type RxFuture<'m> = impl Future> + 'm where RS: 'm; + type RxFuture<'m> = impl Future> + 'm where Self: 'm; fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { async move { self.do_rx(config, buf).await } }