From feb840c503a7110db5d12c0f49eea10ac92aa1c1 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 12:40:39 +0200 Subject: [PATCH 001/374] First iteration attempt on implementing generic flash mutation access for RP2040 --- embassy-rp/Cargo.toml | 2 + embassy-rp/src/flash.rs | 100 ++++++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 1 + 3 files changed, 103 insertions(+) create mode 100644 embassy-rp/src/flash.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f652..51cdda7b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -44,6 +44,8 @@ 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"] } +rp2040-flash = { version = "0.1" } +embedded-storage = { version = "0.3" } 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/flash.rs b/embassy-rp/src/flash.rs new file mode 100644 index 00000000..3b640509 --- /dev/null +++ b/embassy-rp/src/flash.rs @@ -0,0 +1,100 @@ +use embedded_storage::nor_flash::{ + ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, +}; + +/// Error type for NVMC operations. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Opration using a location not in flash. + OutOfBounds, + /// Unaligned operation or using unaligned buffers. + Unaligned, +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Self::Unaligned => NorFlashErrorKind::NotAligned, + } + } +} + +pub struct Flash; + +impl ErrorType for Flash { + type Error = Error; +} + +impl MultiwriteNorFlash for Flash {} + +impl ReadNorFlash for Flash { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + + let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl NorFlash for Flash { + const WRITE_SIZE: usize = 4; + + const ERASE_SIZE: usize = 4096; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to < from || to as usize > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let len = to - from; + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { + return Err(Error::Unaligned); + } + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf56..d86162fb 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -14,6 +14,7 @@ pub mod uart; pub mod usb; mod clocks; +pub mod flash; mod reset; // Reexports From 7bb9620b1ae2a6290b98769835e339a4157d2d47 Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Thu, 15 Sep 2022 12:34:17 +0200 Subject: [PATCH 002/374] 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 1ab0438a..38629a93 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 d67c3c5e..a81ee118 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 a7fa4389..5f6dabb3 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 e5af4c4bcef8a826d14f741fd72ab99ad930a626 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:32:43 +0200 Subject: [PATCH 003/374] 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 75a6e8dd..a792cf07 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 3f672c8a934602d444061ab0880ed415777d21f6 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:44:33 +0200 Subject: [PATCH 004/374] 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 a792cf07..92101c6b 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 f46b83874693382b891f0449c412b4f8591b81d8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:45:03 +0200 Subject: [PATCH 005/374] 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 fa24d526..ae1b4428 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 ccb2f652..1e26f7dd 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 aebbbf56..8dcefece 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 18a92b09..17393322 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 5d1576ea736e91949134b56c786bcc42822cc17e Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 16:45:59 +0200 Subject: [PATCH 006/374] 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 1e26f7dd..7315d673 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 81298394b5f831197bd758d2e6bd665b7257bb73 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 14:54:24 -0700 Subject: [PATCH 007/374] 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 a0328302..9b9a0811 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 c14527486d7b3d4b2c66004c922c40f7819e0411 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:02:05 -0700 Subject: [PATCH 008/374] 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 acf33822..b256cc2f 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 74f0b04d..e7cd9992 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 4322293f634b922c1d12dd544fcdc04e56df1292 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:23:17 -0700 Subject: [PATCH 009/374] 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 b256cc2f..7ad1a6bf 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 e7cd9992..3cf82357 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 54ba4725408bca51c5997d16b58cf334b519ac24 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 09:42:40 +0200 Subject: [PATCH 010/374] 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 4a2b112a..015dd58d 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 9031997c..8266206b 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 bb5d3e53..294464d1 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 334dfcdb65f2ec9c5686923cb1f854492fae02f0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 14:03:04 +0200 Subject: [PATCH 011/374] 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 015dd58d..3d359533 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 0db1332da82afa2ac7a58c15403ad6c5d0f367f4 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 06:45:27 +0200 Subject: [PATCH 012/374] 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 7315d673..63997c99 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -46,6 +46,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 3ad1e5d8..1c446f38 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 8dcefece..c3976d26 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; #[cfg(feature = "time-driver")] pub mod timer; @@ -85,6 +86,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 00000000..b3c78dd4 --- /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 00000000..92770e98 --- /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 00000000..d4a3bab2 --- /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 00000000..7f3bbbe7 --- /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 7412a859fd7089b874b4c162b8e3cc822f09ab22 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:28:56 +0200 Subject: [PATCH 013/374] 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 3d359533..96878ace 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 0c14781a..385e089f 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 39f08051..edba39cc 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 0c6f2786..a12a3a3a 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 2483dcb2..90ba0d1d 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 8eebc798..4d30550d 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 f3b3ca0c..d7bd2170 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 c3976d26..9ce09064 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 30ff02d5..0392e808 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 25150e8a..80bb907a 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 5b262098..4edc883f 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 5fee60bb..8b181aec 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 f3de2ec1..27b536a6 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 b165804e..ca7dde62 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 133a3e67..7a404a91 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 5394bf0c..1373f277 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 352660b5..33ca380f 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 7fdb0b68..4761fcf6 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 7cd2ece1..f1b57a17 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 a68edb32..f9526cbc 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 d62d7e52..c646c0bb 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 2df7f62f..166ffe17 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 74be1f59..00cf3e93 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 00ff67f3..27d7c29c 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 2c8706e4..c96a83ea 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 7d763e7f..0aca6f1c 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 9143e64d..7f34dd30 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 8f674d79..3c60a8de 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 f5e342ed..1ec19e58 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 2fed9f949a28f197e8dbc72fa923c71679dd19c2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:42:49 +0200 Subject: [PATCH 014/374] 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 25a0d7db..400d973f 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 4d11244b..e28fa2c1 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 8d2dd4bc..3a761075 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 0fa87360..f8fff3e2 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 78b09a49..f2e33493 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 c3cba247..fec875cb 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 d99f592b..b418be9d 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 762e0971..253c85c3 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 c97cb165..ea0a1703 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 42da51d0..e0caeaae 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 9bc89eb3..d1c82423 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 2955182e..51cd73a4 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 d520fd68..7a7f61b5 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 3de5a896..bc871064 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 3d4af753..7c6ca1d3 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 c250e24c..5f9c4f17 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 688326e9..0685d419 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 7ad1a6bf..410c4866 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 82eafdef..a7ec5fb7 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 fb9dc9d0..ff9157d5 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 db492446..b7c89931 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 520f2ab9..10fc4a75 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 67758c49..a8bc6385 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 5f6dabb3..46c49a99 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 db965824..e5ee1181 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 92101c6b..fcf056d3 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 34201d03..b4d99513 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 7d1ad87c..cf8b2f80 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 9ae030d0..2a28f276 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 4f33cc5d1a0c5eb4ce9044f477f879ac0445ec36 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:48:35 +0200 Subject: [PATCH 015/374] 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 63997c99..885a4746 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 3cf82357..03293e06 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 b4c19f32..484496f2 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 ff9157d5..20e1a407 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 02e6020b..556d1230 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 4761fcf6..70318b78 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 f1b57a17..65fbda1c 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 f9526cbc..a740b4e0 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 17393322..24c3cdd6 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 00cf3e93..bf92a163 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 33ac63db..895e043d 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 a9c46068..a14e728b 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 208f3908..27f5c260 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 d3702fc3..b9fd20e2 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 c451bd22..05945f6b 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 0aca6f1c..fa92ceae 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 b576a735..7484dc83 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 4d6877cc..11ecb916 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 1eeaac1f..f20b8fcb 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 816778e3fadf31772413ac7bd78a3eeaead66cad Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 06:38:47 +0200 Subject: [PATCH 016/374] 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 885a4746..df0af8df 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 00000000..9e6624cf --- /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 9ce09064..9ac98d22 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 00000000..93a3632a --- /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 9d674f0212bf2a51fccd192632a16f104ba9629c Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 12:40:39 +0200 Subject: [PATCH 017/374] First iteration attempt on implementing generic flash mutation access for RP2040 --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/flash.rs | 100 ++++++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 1 + 3 files changed, 102 insertions(+) create mode 100644 embassy-rp/src/flash.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index df0af8df..ce78acd3 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-storage = { version = "0.3" } 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/flash.rs b/embassy-rp/src/flash.rs new file mode 100644 index 00000000..3b640509 --- /dev/null +++ b/embassy-rp/src/flash.rs @@ -0,0 +1,100 @@ +use embedded_storage::nor_flash::{ + ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, +}; + +/// Error type for NVMC operations. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Opration using a location not in flash. + OutOfBounds, + /// Unaligned operation or using unaligned buffers. + Unaligned, +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Self::Unaligned => NorFlashErrorKind::NotAligned, + } + } +} + +pub struct Flash; + +impl ErrorType for Flash { + type Error = Error; +} + +impl MultiwriteNorFlash for Flash {} + +impl ReadNorFlash for Flash { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + + let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl NorFlash for Flash { + const WRITE_SIZE: usize = 4; + + const ERASE_SIZE: usize = 4096; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to < from || to as usize > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let len = to - from; + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { + return Err(Error::Unaligned); + } + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ac98d22..ce013b09 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -19,6 +19,7 @@ pub mod uart; pub mod usb; mod clocks; +pub mod flash; mod reset; // Reexports From 18dc0dea636daa6e48aa9208b5a74cdfedc8b513 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 08:12:32 +0200 Subject: [PATCH 018/374] Drop rp2040-flash as dependency, as they pull in rp2040-hal for rom-data functions, which are now part of this HAL as well --- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/flash.rs | 335 +++++++++++++++++++++++++++++++++++----- 2 files changed, 295 insertions(+), 42 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 410c4866..fd281fd5 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -191,7 +191,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { } } -const CHANNEL_COUNT: usize = 12; +pub(crate) const CHANNEL_COUNT: usize = 12; const NEW_AW: AtomicWaker = AtomicWaker::new(); static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 3b640509..0fc19f5a 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,7 +1,18 @@ use embedded_storage::nor_flash::{ - ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, + check_erase, check_read, check_write, ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +pub const FLASH_BASE: usize = 0x10000000; + +// **NOTE**: +// +// These limitations are currently enforced because of using the +// RP2040 boot-rom flash functions, that are optimized for flash compatibility +// rather than performance. +pub const WRITE_SIZE: usize = 256; +pub const READ_SIZE: usize = 1; +pub const ERASE_SIZE: usize = 4096; + /// Error type for NVMC operations. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -10,6 +21,17 @@ pub enum Error { OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, + Other, +} + +impl From for Error { + fn from(e: NorFlashErrorKind) -> Self { + match e { + NorFlashErrorKind::NotAligned => Self::Unaligned, + NorFlashErrorKind::OutOfBounds => Self::OutOfBounds, + _ => Self::Other, + } + } } impl NorFlashError for Error { @@ -17,27 +39,57 @@ impl NorFlashError for Error { match self { Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, Self::Unaligned => NorFlashErrorKind::NotAligned, + Self::Other => NorFlashErrorKind::Other, } } } pub struct Flash; +impl Flash { + /// Make sure to uphold the contract points with rp2040-flash. + /// - interrupts must be disabled + /// - DMA must not access flash memory + unsafe fn in_ram(&mut self, operation: impl FnOnce()) { + let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; + + // TODO: Make sure CORE1 is paused during the entire duration of the RAM function + + critical_section::with(|_| { + // Pause all DMA channels for the duration of the ram operation + for (number, status) in dma_status.iter_mut().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + *status = ch.ctrl_trig().read().en(); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(false)); + } + } + + // Run our flash operation in RAM + operation(); + + // Re-enable previously enabled DMA channels + for (number, status) in dma_status.iter().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(true)); + } + } + }); + } +} impl ErrorType for Flash { type Error = Error; } -impl MultiwriteNorFlash for Flash {} - impl ReadNorFlash for Flash { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { - return Err(Error::OutOfBounds); - } + check_read(self, offset, bytes.len())?; + + let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) }; - let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } @@ -48,53 +100,254 @@ impl ReadNorFlash for Flash { } impl NorFlash for Flash { - const WRITE_SIZE: usize = 4; + const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = 4096; + const ERASE_SIZE: usize = ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - if to < from || to as usize > FLASH_SIZE { - return Err(Error::OutOfBounds); - } - if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { - return Err(Error::Unaligned); - } + check_erase(self, from, to)?; let len = to - from; - // Make sure to uphold the contract point with rp2040-flash. - // - interrupts must be disabled - // - DMA must not access flash memory - // FIXME: Pause all DMA channels for the duration of the flash_write? - - critical_section::with(|_| { - unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; - }); - - // Re-enable DMA channels + unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; Ok(()) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - if offset as usize + bytes.len() > FLASH_SIZE { - return Err(Error::OutOfBounds); - } - if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { - return Err(Error::Unaligned); - } + check_write(self, offset, bytes.len())?; - // Make sure to uphold the contract point with rp2040-flash. - // - interrupts must be disabled - // - DMA must not access flash memory - // FIXME: Pause all DMA channels for the duration of the flash_write? + trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset); - critical_section::with(|_| { - unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; - }); - - // Re-enable DMA channels + unsafe { self.in_ram(|| ram_helpers::flash_range_program(offset, bytes, true)) }; Ok(()) } } + +mod ram_helpers { + use core::marker::PhantomData; + + use crate::rom_data; + + #[repr(C)] + struct FlashFunctionPointers<'a> { + connect_internal_flash: unsafe extern "C" fn() -> (), + flash_exit_xip: unsafe extern "C" fn() -> (), + flash_range_erase: Option ()>, + flash_range_program: Option ()>, + flash_flush_cache: unsafe extern "C" fn() -> (), + flash_enter_cmd_xip: unsafe extern "C" fn() -> (), + phantom: PhantomData<&'a ()>, + } + + #[allow(unused)] + fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> { + FlashFunctionPointers { + connect_internal_flash: rom_data::connect_internal_flash::ptr(), + flash_exit_xip: rom_data::flash_exit_xip::ptr(), + flash_range_erase: if erase { + Some(rom_data::flash_range_erase::ptr()) + } else { + None + }, + flash_range_program: if write { + Some(rom_data::flash_range_program::ptr()) + } else { + None + }, + flash_flush_cache: rom_data::flash_flush_cache::ptr(), + flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(), + phantom: PhantomData, + } + } + + #[allow(unused)] + /// # Safety + /// + /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode + unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers { + let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1); + let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr); + FlashFunctionPointers { + connect_internal_flash: rom_data::connect_internal_flash::ptr(), + flash_exit_xip: rom_data::flash_exit_xip::ptr(), + flash_range_erase: if erase { + Some(rom_data::flash_range_erase::ptr()) + } else { + None + }, + flash_range_program: if write { + Some(rom_data::flash_range_program::ptr()) + } else { + None + }, + flash_flush_cache: rom_data::flash_flush_cache::ptr(), + flash_enter_cmd_xip: boot2_fn, + phantom: PhantomData, + } + } + + /// Erase a flash range starting at `addr` with length `len`. + /// + /// `addr` and `len` must be multiples of 4096 + /// + /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// is used to re-initialize the XIP engine after flashing. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// `addr` and `len` parameters must be valid and are not checked. + pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(true, false, &boot2) + } else { + flash_function_pointers(true, false) + }; + write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers); + } + + /// Erase and rewrite a flash range starting at `addr` with data `data`. + /// + /// `addr` and `data.len()` must be multiples of 4096 + /// + /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// is used to re-initialize the XIP engine after flashing. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// `addr` and `len` parameters must be valid and are not checked. + pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(true, true, &boot2) + } else { + flash_function_pointers(true, true) + }; + write_flash_inner( + addr, + data.len() as u32, + Some(data), + &ptrs as *const FlashFunctionPointers, + ); + } + + /// Write a flash range starting at `addr` with data `data`. + /// + /// `addr` and `data.len()` must be multiples of 256 + /// + /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// is used to re-initialize the XIP engine after flashing. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// `addr` and `len` parameters must be valid and are not checked. + pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(false, true, &boot2) + } else { + flash_function_pointers(false, true) + }; + write_flash_inner( + addr, + data.len() as u32, + Some(data), + &ptrs as *const FlashFunctionPointers, + ); + } + + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// Length of data must be a multiple of 4096 + /// addr must be aligned to 4096 + #[inline(never)] + #[link_section = ".data.ram_func"] + unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { + /* + Should be equivalent to: + rom_data::connect_internal_flash(); + rom_data::flash_exit_xip(); + rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected + rom_data::flash_range_program(addr, data as *const _, len); // if selected + rom_data::flash_flush_cache(); + rom_data::flash_enter_cmd_xip(); + */ + core::arch::asm!( + "mov r8, r0", + "mov r9, r2", + "mov r10, r1", + "ldr r4, [{ptrs}, #0]", + "blx r4", // connect_internal_flash() + + "ldr r4, [{ptrs}, #4]", + "blx r4", // flash_exit_xip() + + "mov r0, r8", // r0 = addr + "mov r1, r10", // r1 = len + "movs r2, #1", + "lsls r2, r2, #31", // r2 = 1 << 31 + "movs r3, #0", // r3 = 0 + "ldr r4, [{ptrs}, #8]", + "cmp r4, #0", + "beq 1f", + "blx r4", // flash_range_erase(addr, len, 1 << 31, 0) + "1:", + + "mov r0, r8", // r0 = addr + "mov r1, r9", // r0 = data + "mov r2, r10", // r2 = len + "ldr r4, [{ptrs}, #12]", + "cmp r4, #0", + "beq 1f", + "blx r4", // flash_range_program(addr, data, len); + "1:", + + "ldr r4, [{ptrs}, #16]", + "blx r4", // flash_flush_cache(); + + "ldr r4, [{ptrs}, #20]", + "blx r4", // flash_enter_cmd_xip(); + ptrs = in(reg) ptrs, + // Registers r8-r15 are not allocated automatically, + // so assign them manually. We need to use them as + // otherwise there are not enough registers available. + in("r0") addr, + in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()), + in("r1") len, + out("r3") _, + out("r4") _, + lateout("r8") _, + lateout("r9") _, + lateout("r10") _, + clobber_abi("C"), + ); + } +} From a89a0c2f122759a7e743e75152f7af7f67b4387b Mon Sep 17 00:00:00 2001 From: ceekdee Date: Tue, 27 Sep 2022 21:55:41 -0500 Subject: [PATCH 019/374] Initial add for sx126x --- README.md | 2 +- embassy-lora/Cargo.toml | 2 + embassy-lora/src/lib.rs | 2 + embassy-lora/src/sx126x/mod.rs | 171 ++++ .../src/sx126x/sx126x_lora/board_specific.rs | 251 ++++++ embassy-lora/src/sx126x/sx126x_lora/mod.rs | 735 ++++++++++++++++++ .../src/sx126x/sx126x_lora/mod_params.rs | 469 +++++++++++ .../src/sx126x/sx126x_lora/subroutine.rs | 688 ++++++++++++++++ examples/nrf/Cargo.toml | 7 +- examples/nrf/src/bin/lora_p2p_report.rs | 84 ++ examples/nrf/src/bin/lora_p2p_sense.rs | 173 +++++ 11 files changed, 2582 insertions(+), 2 deletions(-) create mode 100644 embassy-lora/src/sx126x/mod.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/board_specific.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod_params.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/subroutine.rs create mode 100644 examples/nrf/src/bin/lora_p2p_report.rs create mode 100644 examples/nrf/src/bin/lora_p2p_sense.rs diff --git a/README.md b/README.md index 9f08bf67..eaa91012 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The embassy-net network stac The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. - **LoRa** - -embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers. +embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. - **USB** - embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 1757efa8..0c2f983f 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -8,6 +8,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ + { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] }, { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] @@ -15,6 +16,7 @@ flavors = [ [lib] [features] +rak4631 = [] sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 90ba0d1d..342f66b2 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -7,6 +7,8 @@ pub(crate) mod fmt; #[cfg(feature = "stm32wl")] pub mod stm32wl; +#[cfg(feature = "rak4631")] +pub mod sx126x; #[cfg(feature = "sx127x")] pub mod sx127x; diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs new file mode 100644 index 00000000..d67c7b10 --- /dev/null +++ b/embassy-lora/src/sx126x/mod.rs @@ -0,0 +1,171 @@ +use core::future::Future; + +use defmt::Format; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::*; +use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; +use lorawan_device::async_device::Timings; + +mod sx126x_lora; +use sx126x_lora::LoRa; + +use self::sx126x_lora::mod_params::RadioError; + +/// Semtech Sx126x LoRa peripheral +pub struct Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + pub lora: LoRa, +} + +impl Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + pub async fn new( + spi: SPI, + cs: CS, + reset: RESET, + antenna_rx: ANTRX, + antenna_tx: ANTTX, + dio1: WAIT, + busy: WAIT, + enable_public_network: bool, + ) -> Result> { + let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); + lora.init().await?; + lora.set_lora_modem(enable_public_network).await?; + Ok(Self { lora }) + } +} + +impl Timings for Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + fn get_rx_window_offset_ms(&self) -> i32 { + -500 + } + fn get_rx_window_duration_ms(&self) -> u32 { + 800 + } +} + +impl PhyRxTx for Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + type PhyError = RadioError; + + type TxFuture<'m> = impl Future> + 'm + where + SPI: 'm, + CS: 'm, + RESET: 'm, + ANTRX: 'm, + ANTTX: 'm, + WAIT: 'm, + BUS: 'm; + + fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> { + trace!("TX START"); + async move { + self.lora + .set_tx_config( + config.pw, + config.rf.spreading_factor.into(), + config.rf.bandwidth.into(), + config.rf.coding_rate.into(), + 4, + false, + true, + false, + 0, + false, + ) + .await?; + self.lora.set_max_payload_length(buffer.len() as u8).await?; + self.lora.set_channel(config.rf.frequency).await?; + self.lora.send(buffer, 0xffffff).await?; + self.lora.process_irq(None, None, None).await?; + trace!("TX DONE"); + return Ok(0); + } + } + + type RxFuture<'m> = impl Future> + 'm + where + SPI: 'm, + CS: 'm, + RESET: 'm, + ANTRX: 'm, + ANTTX: 'm, + WAIT: 'm, + BUS: 'm; + + fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> { + trace!("RX START"); + async move { + self.lora + .set_rx_config( + config.spreading_factor.into(), + config.bandwidth.into(), + config.coding_rate.into(), + 4, + 4, + false, + 0u8, + true, + false, + 0, + false, + true, + ) + .await?; + self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; + self.lora.set_channel(config.frequency).await?; + self.lora.rx(90 * 1000).await?; + let mut received_len = 0u8; + self.lora + .process_irq(Some(receiving_buffer), Some(&mut received_len), None) + .await?; + trace!("RX DONE"); + + let packet_status = self.lora.get_latest_packet_status(); + let mut rssi = 0i16; + let mut snr = 0i8; + if packet_status.is_some() { + rssi = packet_status.unwrap().rssi as i16; + snr = packet_status.unwrap().snr; + } + + Ok((received_len as usize, RxQuality::new(rssi, snr))) + } + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs new file mode 100644 index 00000000..5b4891c0 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -0,0 +1,251 @@ +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +use super::mod_params::RadioError::*; +use super::mod_params::*; +use super::LoRa; + +// Defines the time required for the TCXO to wakeup [ms]. +const BRD_TCXO_WAKEUP_TIME: u32 = 10; + +// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. +// The base implementation supports the RAK4631 board. + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. + pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Initialize the TCXO power pin + pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError> { + let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; + self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) + .await?; + Ok(()) + } + + // Initialize RF switch control pins + pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError> { + self.sub_set_dio2_as_rf_switch_ctrl(true).await?; + Ok(()) + } + + // Initialize the radio debug pins + pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Hardware reset of the radio + pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError> { + Timer::after(Duration::from_millis(10)).await; + self.reset.set_low().map_err(|_| Reset)?; + Timer::after(Duration::from_millis(20)).await; + self.reset.set_high().map_err(|_| Reset)?; + Timer::after(Duration::from_millis(10)).await; + Ok(()) + } + + // Wait while the busy pin is high + pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError> { + self.busy.wait_for_low().await.map_err(|_| Busy)?; + Ok(()) + } + + // Wake up the radio + pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError> { + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; + self.spi.write(&[0x00]).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + self.brd_set_operating_mode(RadioMode::StandbyRC); + Ok(()) + } + + // Send a command that writes data to the radio + pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[op_code.value()]).await.map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + if op_code != OpCode::SetSleep { + self.brd_wait_on_busy().await?; + } + Ok(()) + } + + // Send a command that reads data from the radio, filling the provided buffer and returning a status + pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result> { + let mut status = [0u8]; + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[op_code.value()]).await.map_err(SPI)?; + self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + + Ok(status[0]) + } + + // Write one or more bytes of data to the radio memory + pub(super) async fn brd_write_registers( + &mut self, + start_register: Register, + buffer: &[u8], + ) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; + self.spi + .write(&[ + ((start_register.addr() & 0xFF00) >> 8) as u8, + (start_register.addr() & 0x00FF) as u8, + ]) + .await + .map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Read one or more bytes of data from the radio memory + pub(super) async fn brd_read_registers( + &mut self, + start_register: Register, + buffer: &mut [u8], + ) -> Result<(), RadioError> { + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; + self.spi + .write(&[ + ((start_register.addr() & 0xFF00) >> 8) as u8, + (start_register.addr() & 0x00FF) as u8, + 0x00u8, + ]) + .await + .map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Write data to the buffer holding the payload in the radio + pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; + self.spi.write(&[offset]).await.map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Read data from the buffer holding the payload in the radio + pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError> { + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; + self.spi.write(&[offset]).await.map_err(SPI)?; + self.spi.write(&[0x00]).await.map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Set the radio output power + pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError> { + self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; + Ok(()) + } + + // Get the radio type + pub(super) fn brd_get_radio_type(&mut self) -> RadioType { + #[cfg(feature = "rak4631")] + RadioType::SX1262 + } + + // Initialize the RF switch I/O pins interface + pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // De-initialize the RF switch I/O pins interface for MCU low power modes + pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Check if the given RF frequency is supported by the hardware + pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { + #[cfg(feature = "rak4631")] + Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 + } + + // Get the duration required for the TCXO to wakeup [ms]. + pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { + BRD_TCXO_WAKEUP_TIME + } + + /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process + pub(super) async fn brd_get_dio1_pin_state( + &mut self, + ) -> Result> { + Ok(0) + } + */ + + // Get the current radio operatiing mode + pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { + self.operating_mode + } + + // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts. + pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { + self.operating_mode = mode; + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs new file mode 100644 index 00000000..02f5f83a --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -0,0 +1,735 @@ +#![allow(dead_code)] + +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +mod board_specific; +pub mod mod_params; +mod subroutine; + +use mod_params::RadioError::*; +use mod_params::*; + +// Syncwords for public and private networks +const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; +const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; + +// Maximum number of registers that can be added to the retention list +const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; + +// Possible LoRa bandwidths +const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; + +// Radio complete wakeup time with margin for temperature compensation [ms] +const RADIO_WAKEUP_TIME: u32 = 3; + +/// Provides high-level access to Semtech SX126x-based boards +pub struct LoRa { + spi: SPI, + cs: CS, + reset: RESET, + antenna_rx: ANTRX, + antenna_tx: ANTTX, + dio1: WAIT, + busy: WAIT, + operating_mode: RadioMode, + rx_continuous: bool, + max_payload_length: u8, + modulation_params: Option, + packet_type: PacketType, + packet_params: Option, + packet_status: Option, + image_calibrated: bool, + frequency_error: u32, +} + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () + pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { + Self { + spi, + cs, + reset, + antenna_rx, + antenna_tx, + dio1, + busy, + operating_mode: RadioMode::Sleep, + rx_continuous: false, + max_payload_length: 0xFFu8, + modulation_params: None, + packet_type: PacketType::LoRa, + packet_params: None, + packet_status: None, + image_calibrated: false, + frequency_error: 0u32, // where is volatile FrequencyError modified ??? + } + } + + /// Initialize the radio + pub async fn init(&mut self) -> Result<(), RadioError> { + self.sub_init().await?; + self.sub_set_standby(StandbyMode::RC).await?; + self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; + self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; + self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + self.add_register_to_retention_list(Register::RxGain.addr()).await?; + self.add_register_to_retention_list(Register::TxModulation.addr()) + .await?; + Ok(()) + } + + /// Return current radio state + pub fn get_status(&mut self) -> RadioState { + match self.brd_get_operating_mode() { + RadioMode::Transmit => RadioState::TxRunning, + RadioMode::Receive => RadioState::RxRunning, + RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, + _ => RadioState::Idle, + } + } + + /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) + pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError> { + self.sub_set_packet_type(PacketType::LoRa).await?; + if enable_public_network { + self.brd_write_registers( + Register::LoRaSyncword, + &[ + ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, + (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, + ], + ) + .await?; + } else { + self.brd_write_registers( + Register::LoRaSyncword, + &[ + ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, + (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, + ], + ) + .await?; + } + + Ok(()) + } + + /// Sets the channel frequency + pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError> { + self.sub_set_rf_frequency(frequency).await?; + Ok(()) + } + + /* Checks if the channel is free for the given time. This is currently not implemented until a substitute + for switching to the FSK modem is found. + + pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; + */ + + /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand. + /// After calling this function either set_rx_config() or set_tx_config() must be called. + pub async fn get_random_value(&mut self) -> Result> { + self.sub_set_dio_irq_params( + IrqMask::None.value(), + IrqMask::None.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + let result = self.sub_get_random().await?; + Ok(result) + } + + /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand. + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// symb_timeout RxSingle timeout value in symbols + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// payload_len payload length when fixed length is used + /// crc_on [0: OFF, 1: ON] + /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] + /// hop_period number of symbols between each hop + /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] + /// rx_continuous reception mode [false: single mode, true: continuous mode] + pub async fn set_rx_config( + &mut self, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + symb_timeout: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + _freq_hop_on: bool, + _hop_period: u8, + iq_inverted: bool, + rx_continuous: bool, + ) -> Result<(), RadioError> { + let mut symb_timeout_final = symb_timeout; + + self.rx_continuous = rx_continuous; + if self.rx_continuous { + symb_timeout_final = 0; + } + if fixed_len { + self.max_payload_length = payload_len; + } else { + self.max_payload_length = 0xFFu8; + } + + self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; + + let mut low_data_rate_optimize = 0x00u8; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = 0x01u8; + } + + let modulation_params = ModulationParams { + spreading_factor: spreading_factor, + bandwidth: bandwidth, + coding_rate: coding_rate, + low_data_rate_optimize: low_data_rate_optimize, + }; + + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let packet_params = PacketParams { + preamble_length: preamble_length_final, + implicit_header: fixed_len, + payload_length: self.max_payload_length, + crc_on: crc_on, + iq_inverted: iq_inverted, + }; + + self.modulation_params = Some(modulation_params); + self.packet_params = Some(packet_params); + + self.standby().await?; + self.sub_set_modulation_params().await?; + self.sub_set_packet_params().await?; + self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; + + // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + let mut iq_polarity = [0x00u8]; + self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; + if iq_inverted { + self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) + .await?; + } else { + self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) + .await?; + } + Ok(()) + } + + /// Set the transmission parameters for the LoRa modem (only). + /// power output power [dBm] + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// crc_on [0: OFF, 1: ON] + /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] + /// hop_period number of symbols between each hop + /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] + pub async fn set_tx_config( + &mut self, + power: i8, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + crc_on: bool, + _freq_hop_on: bool, + _hop_period: u8, + iq_inverted: bool, + ) -> Result<(), RadioError> { + let mut low_data_rate_optimize = 0x00u8; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = 0x01u8; + } + + let modulation_params = ModulationParams { + spreading_factor: spreading_factor, + bandwidth: bandwidth, + coding_rate: coding_rate, + low_data_rate_optimize: low_data_rate_optimize, + }; + + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let packet_params = PacketParams { + preamble_length: preamble_length_final, + implicit_header: fixed_len, + payload_length: self.max_payload_length, + crc_on: crc_on, + iq_inverted: iq_inverted, + }; + + self.modulation_params = Some(modulation_params); + self.packet_params = Some(packet_params); + + self.standby().await?; + self.sub_set_modulation_params().await?; + self.sub_set_packet_params().await?; + + // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) + + let mut tx_modulation = [0x00u8]; + self.brd_read_registers(Register::TxModulation, &mut tx_modulation) + .await?; + if bandwidth == Bandwidth::_500KHz { + self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) + .await?; + } else { + self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) + .await?; + } + + self.brd_set_rf_tx_power(power).await?; + Ok(()) + } + + /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] + pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result> { + Ok(self.brd_check_rf_frequency(frequency).await?) + } + + /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called) + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// payload_len sets payload length when fixed length is used + /// crc_on [0: OFF, 1: ON] + pub fn get_time_on_air( + &mut self, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + ) -> Result> { + let numerator = 1000 + * Self::get_lora_time_on_air_numerator( + spreading_factor, + bandwidth, + coding_rate, + preamble_length, + fixed_len, + payload_len, + crc_on, + ); + let denominator = bandwidth.value_in_hz(); + if denominator == 0 { + Err(RadioError::InvalidBandwidth) + } else { + Ok((numerator + denominator - 1) / denominator) + } + } + + /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] + pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError> { + if self.packet_params.is_some() { + self.sub_set_dio_irq_params( + IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), + IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + let mut packet_params = self.packet_params.as_mut().unwrap(); + packet_params.payload_length = buffer.len() as u8; + self.sub_set_packet_params().await?; + self.sub_send_payload(buffer, timeout).await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + /// Set the radio in sleep mode + pub async fn sleep(&mut self) -> Result<(), RadioError> { + self.sub_set_sleep(SleepParams { + wakeup_rtc: false, + reset: false, + warm_start: true, + }) + .await?; + Timer::after(Duration::from_millis(2)).await; + Ok(()) + } + + /// Set the radio in standby mode + pub async fn standby(&mut self) -> Result<(), RadioError> { + self.sub_set_standby(StandbyMode::RC).await?; + Ok(()) + } + + /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)] + pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + if self.rx_continuous { + self.sub_set_rx(0xFFFFFF).await?; + } else { + self.sub_set_rx(timeout << 6).await?; + } + + Ok(()) + } + + /// Start a Channel Activity Detection + pub async fn start_cad(&mut self) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), + IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + self.sub_set_cad().await?; + Ok(()) + } + + /// Sets the radio in continuous wave transmission mode + /// frequency channel RF frequency + /// power output power [dBm] + /// timeout transmission mode timeout [s] + pub async fn set_tx_continuous_wave( + &mut self, + frequency: u32, + power: i8, + _timeout: u16, + ) -> Result<(), RadioError> { + self.sub_set_rf_frequency(frequency).await?; + self.brd_set_rf_tx_power(power).await?; + self.sub_set_tx_continuous_wave().await?; + + Ok(()) + } + + /// Read the current RSSI value for the LoRa modem (only) [dBm] + pub async fn get_rssi(&mut self) -> Result> { + let value = self.sub_get_rssi_inst().await?; + Ok(value as i16) + } + + /// Write one or more radio registers with a buffer of a given size, starting at the first register address + pub async fn write_registers_from_buffer( + &mut self, + start_register: Register, + buffer: &[u8], + ) -> Result<(), RadioError> { + self.brd_write_registers(start_register, buffer).await?; + Ok(()) + } + + /// Read one or more radio registers into a buffer of a given size, starting at the first register address + pub async fn read_registers_into_buffer( + &mut self, + start_register: Register, + buffer: &mut [u8], + ) -> Result<(), RadioError> { + self.brd_read_registers(start_register, buffer).await?; + Ok(()) + } + + /// Set the maximum payload length (in bytes) for a LoRa modem (only). + pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError> { + if self.packet_params.is_some() { + let packet_params = self.packet_params.as_mut().unwrap(); + self.max_payload_length = max; + packet_params.payload_length = max; + self.sub_set_packet_params().await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + /// Get the time required for the board plus radio to get out of sleep [ms] + pub fn get_wakeup_time(&mut self) -> u32 { + self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME + } + + /// Process the radio irq + pub async fn process_irq( + &mut self, + receiving_buffer: Option<&mut [u8]>, + received_len: Option<&mut u8>, + cad_activity_detected: Option<&mut bool>, + ) -> Result<(), RadioError> { + loop { + info!("process_irq loop entered"); // debug ??? + + let de = self.sub_get_device_errors().await?; + info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", + de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); + let st = self.sub_get_status().await?; + info!( + "radio status: cmd_status: {:x}, chip_mode: {:x}", + st.cmd_status, st.chip_mode + ); + + self.dio1.wait_for_high().await.map_err(|_| DIO1)?; + let operating_mode = self.brd_get_operating_mode(); + let irq_flags = self.sub_get_irq_status().await?; + self.sub_clear_irq_status(irq_flags).await?; + info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? + + // check for errors and unexpected interrupt masks (based on operation mode) + if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } + return Err(RadioError::HeaderError); + } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { + if operating_mode == RadioMode::Receive { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } + return Err(RadioError::CRCErrorOnReceive); + } else { + return Err(RadioError::CRCErrorUnexpected); + } + } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { + if operating_mode == RadioMode::Transmit { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Err(RadioError::TransmitTimeout); + } else if operating_mode == RadioMode::Receive { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Err(RadioError::ReceiveTimeout); + } else { + return Err(RadioError::TimeoutUnexpected); + } + } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) + && (operating_mode != RadioMode::Transmit) + { + return Err(RadioError::TransmitDoneUnexpected); + } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) + && (operating_mode != RadioMode::Receive) + { + return Err(RadioError::ReceiveDoneUnexpected); + } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) + || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) + && (operating_mode != RadioMode::ChannelActivityDetection) + { + return Err(RadioError::CADUnexpected); + } + + // debug ??? + if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { + info!("HeaderValid"); + } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { + info!("PreambleDetected"); + } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { + info!("SyncwordValid"); + } + + // handle completions + if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Ok(()); + } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + + // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) + self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; + let mut evt_clr = [0x00u8]; + self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; + evt_clr[0] |= 1 << 1; + self.brd_write_registers(Register::EvtClr, &evt_clr).await?; + } + + if receiving_buffer.is_some() && received_len.is_some() { + *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; + } + self.packet_status = self.sub_get_packet_status().await?.into(); + return Ok(()); + } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { + if cad_activity_detected.is_some() { + *(cad_activity_detected.unwrap()) = + (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); + } + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Ok(()); + } + + // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid + // are in that category), loop to wait again + } + } + + // SX126x-specific functions + + /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] + pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + if self.rx_continuous { + self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous + } else { + self.sub_set_rx_boosted(timeout << 6).await?; + } + + Ok(()) + } + + /// Set the Rx duty cycle management parameters (SX126x radios only) + /// rx_time structure describing reception timeout value + /// sleep_time structure describing sleep timeout value + pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { + self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; + Ok(()) + } + + pub fn get_latest_packet_status(&mut self) -> Option { + self.packet_status + } + + // Utilities + + async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError> { + let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; + + // Read the address and registers already added to the list + self.brd_read_registers(Register::RetentionList, &mut buffer).await?; + + let number_of_registers = buffer[0]; + for i in 0..number_of_registers { + if register_address + == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) + { + return Ok(()); // register already in list + } + } + + if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { + buffer[0] += 1; // increment number of registers + + buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; + buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; + self.brd_write_registers(Register::RetentionList, &buffer).await?; + + Ok(()) + } else { + Err(RadioError::RetentionListExceeded) + } + } + + fn get_lora_time_on_air_numerator( + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + ) -> u32 { + let cell_denominator; + let cr_denominator = (coding_rate.value() as i32) + 4; + + // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let mut low_data_rate_optimize = false; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = true; + } + + let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) + - (4 * spreading_factor.value() as i32) + + (if fixed_len { 0 } else { 20 }); + + if spreading_factor.value() <= 6 { + cell_denominator = 4 * (spreading_factor.value() as i32); + } else { + cell_numerator += 8; + if low_data_rate_optimize { + cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); + } else { + cell_denominator = 4 * (spreading_factor.value() as i32); + } + } + + if cell_numerator < 0 { + cell_numerator = 0; + } + + let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) + + (preamble_length_final as i32) + + 12; + + if spreading_factor.value() <= 6 { + intermediate = intermediate + 2; + } + + (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs new file mode 100644 index 00000000..e270b2a0 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs @@ -0,0 +1,469 @@ +use core::fmt::Debug; + +use lorawan_device::async_device::radio as device; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RadioError { + SPI(BUS), + CS, + Reset, + AntRx, + AntTx, + Busy, + DIO1, + PayloadSizeMismatch(usize, usize), + RetentionListExceeded, + InvalidBandwidth, + ModulationParamsMissing, + PacketParamsMissing, + HeaderError, + CRCErrorUnexpected, + CRCErrorOnReceive, + TransmitTimeout, + ReceiveTimeout, + TimeoutUnexpected, + TransmitDoneUnexpected, + ReceiveDoneUnexpected, + CADUnexpected, +} + +pub struct RadioSystemError { + pub rc_64khz_calibration: bool, + pub rc_13mhz_calibration: bool, + pub pll_calibration: bool, + pub adc_calibration: bool, + pub image_calibration: bool, + pub xosc_start: bool, + pub pll_lock: bool, + pub pa_ramp: bool, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum PacketType { + GFSK = 0x00, + LoRa = 0x01, + None = 0x0F, +} + +impl PacketType { + pub const fn value(self) -> u8 { + self as u8 + } + pub fn to_enum(value: u8) -> Self { + if value == 0x00 { + PacketType::GFSK + } else if value == 0x01 { + PacketType::LoRa + } else { + PacketType::None + } + } +} + +#[derive(Clone, Copy)] +pub struct PacketStatus { + pub rssi: i8, + pub snr: i8, + pub signal_rssi: i8, + pub freq_error: u32, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum RadioType { + SX1261, + SX1262, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum RadioMode { + Sleep = 0x00, // sleep mode + StandbyRC = 0x01, // standby mode with RC oscillator + StandbyXOSC = 0x02, // standby mode with XOSC oscillator + FrequencySynthesis = 0x03, // frequency synthesis mode + Transmit = 0x04, // transmit mode + Receive = 0x05, // receive mode + ReceiveDutyCycle = 0x06, // receive duty cycle mode + ChannelActivityDetection = 0x07, // channel activity detection mode +} + +impl RadioMode { + /// Returns the value of the mode. + pub const fn value(self) -> u8 { + self as u8 + } + pub fn to_enum(value: u8) -> Self { + if value == 0x00 { + RadioMode::Sleep + } else if value == 0x01 { + RadioMode::StandbyRC + } else if value == 0x02 { + RadioMode::StandbyXOSC + } else if value == 0x03 { + RadioMode::FrequencySynthesis + } else if value == 0x04 { + RadioMode::Transmit + } else if value == 0x05 { + RadioMode::Receive + } else if value == 0x06 { + RadioMode::ReceiveDutyCycle + } else if value == 0x07 { + RadioMode::ChannelActivityDetection + } else { + RadioMode::Sleep + } + } +} + +pub enum RadioState { + Idle = 0x00, + RxRunning = 0x01, + TxRunning = 0x02, + ChannelActivityDetecting = 0x03, +} + +impl RadioState { + /// Returns the value of the state. + pub fn value(self) -> u8 { + self as u8 + } +} + +pub struct RadioStatus { + pub cmd_status: u8, + pub chip_mode: u8, +} + +impl RadioStatus { + pub fn value(self) -> u8 { + (self.chip_mode << 4) | (self.cmd_status << 1) + } +} + +#[derive(Clone, Copy)] +pub enum IrqMask { + None = 0x0000, + TxDone = 0x0001, + RxDone = 0x0002, + PreambleDetected = 0x0004, + SyncwordValid = 0x0008, + HeaderValid = 0x0010, + HeaderError = 0x0020, + CRCError = 0x0040, + CADDone = 0x0080, + CADActivityDetected = 0x0100, + RxTxTimeout = 0x0200, + All = 0xFFFF, +} + +impl IrqMask { + pub fn value(self) -> u16 { + self as u16 + } +} + +#[derive(Clone, Copy)] +pub enum Register { + PacketParams = 0x0704, // packet configuration + PayloadLength = 0x0702, // payload size + SynchTimeout = 0x0706, // recalculated number of symbols + Syncword = 0x06C0, // Syncword values + LoRaSyncword = 0x0740, // LoRa Syncword value + GeneratedRandomNumber = 0x0819, //32-bit generated random number + AnaLNA = 0x08E2, // disable the LNA + AnaMixer = 0x08E5, // disable the mixer + RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted) + XTATrim = 0x0911, // device internal trimming capacitor + OCP = 0x08E7, // over current protection max value + RetentionList = 0x029F, // retention list + IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) + TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) + RTCCtrl = 0x0902, // RTC control + EvtClr = 0x0944, // event clear +} + +impl Register { + pub fn addr(self) -> u16 { + self as u16 + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum OpCode { + GetStatus = 0xC0, + WriteRegister = 0x0D, + ReadRegister = 0x1D, + WriteBuffer = 0x0E, + ReadBuffer = 0x1E, + SetSleep = 0x84, + SetStandby = 0x80, + SetFS = 0xC1, + SetTx = 0x83, + SetRx = 0x82, + SetRxDutyCycle = 0x94, + SetCAD = 0xC5, + SetTxContinuousWave = 0xD1, + SetTxContinuousPremable = 0xD2, + SetPacketType = 0x8A, + GetPacketType = 0x11, + SetRFFrequency = 0x86, + SetTxParams = 0x8E, + SetPAConfig = 0x95, + SetCADParams = 0x88, + SetBufferBaseAddress = 0x8F, + SetModulationParams = 0x8B, + SetPacketParams = 0x8C, + GetRxBufferStatus = 0x13, + GetPacketStatus = 0x14, + GetRSSIInst = 0x15, + GetStats = 0x10, + ResetStats = 0x00, + CfgDIOIrq = 0x08, + GetIrqStatus = 0x12, + ClrIrqStatus = 0x02, + Calibrate = 0x89, + CalibrateImage = 0x98, + SetRegulatorMode = 0x96, + GetErrors = 0x17, + ClrErrors = 0x07, + SetTCXOMode = 0x97, + SetTxFallbackMode = 0x93, + SetRFSwitchMode = 0x9D, + SetStopRxTimerOnPreamble = 0x9F, + SetLoRaSymbTimeout = 0xA0, +} + +impl OpCode { + pub fn value(self) -> u8 { + self as u8 + } +} + +pub struct SleepParams { + pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC + pub reset: bool, + pub warm_start: bool, +} + +impl SleepParams { + pub fn value(self) -> u8 { + ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum StandbyMode { + RC = 0x00, + XOSC = 0x01, +} + +impl StandbyMode { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum RegulatorMode { + UseLDO = 0x00, + UseDCDC = 0x01, +} + +impl RegulatorMode { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub struct CalibrationParams { + pub rc64k_enable: bool, // calibrate RC64K clock + pub rc13m_enable: bool, // calibrate RC13M clock + pub pll_enable: bool, // calibrate PLL + pub adc_pulse_enable: bool, // calibrate ADC Pulse + pub adc_bulkn_enable: bool, // calibrate ADC bulkN + pub adc_bulkp_enable: bool, // calibrate ADC bulkP + pub img_enable: bool, +} + +impl CalibrationParams { + pub fn value(self) -> u8 { + ((self.img_enable as u8) << 6) + | ((self.adc_bulkp_enable as u8) << 5) + | ((self.adc_bulkn_enable as u8) << 4) + | ((self.adc_pulse_enable as u8) << 3) + | ((self.pll_enable as u8) << 2) + | ((self.rc13m_enable as u8) << 1) + | ((self.rc64k_enable as u8) << 0) + } +} + +#[derive(Clone, Copy)] +pub enum TcxoCtrlVoltage { + Ctrl1V6 = 0x00, + Ctrl1V7 = 0x01, + Ctrl1V8 = 0x02, + Ctrl2V2 = 0x03, + Ctrl2V4 = 0x04, + Ctrl2V7 = 0x05, + Ctrl3V0 = 0x06, + Ctrl3V3 = 0x07, +} + +impl TcxoCtrlVoltage { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum RampTime { + Ramp10Us = 0x00, + Ramp20Us = 0x01, + Ramp40Us = 0x02, + Ramp80Us = 0x03, + Ramp200Us = 0x04, + Ramp800Us = 0x05, + Ramp1700Us = 0x06, + Ramp3400Us = 0x07, +} + +impl RampTime { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum SpreadingFactor { + _5 = 0x05, + _6 = 0x06, + _7 = 0x07, + _8 = 0x08, + _9 = 0x09, + _10 = 0x0A, + _11 = 0x0B, + _12 = 0x0C, +} + +impl SpreadingFactor { + pub fn value(self) -> u8 { + self as u8 + } +} + +impl From for SpreadingFactor { + fn from(sf: device::SpreadingFactor) -> Self { + match sf { + device::SpreadingFactor::_7 => SpreadingFactor::_7, + device::SpreadingFactor::_8 => SpreadingFactor::_8, + device::SpreadingFactor::_9 => SpreadingFactor::_9, + device::SpreadingFactor::_10 => SpreadingFactor::_10, + device::SpreadingFactor::_11 => SpreadingFactor::_11, + device::SpreadingFactor::_12 => SpreadingFactor::_12, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum Bandwidth { + _500KHz = 0x06, + _250KHz = 0x05, + _125KHz = 0x04, +} + +impl Bandwidth { + pub fn value(self) -> u8 { + self as u8 + } + + pub fn value_in_hz(self) -> u32 { + match self { + Bandwidth::_125KHz => 125000u32, + Bandwidth::_250KHz => 250000u32, + Bandwidth::_500KHz => 500000u32, + } + } +} + +impl From for Bandwidth { + fn from(bw: device::Bandwidth) -> Self { + match bw { + device::Bandwidth::_500KHz => Bandwidth::_500KHz, + device::Bandwidth::_250KHz => Bandwidth::_250KHz, + device::Bandwidth::_125KHz => Bandwidth::_125KHz, + } + } +} + +#[derive(Clone, Copy)] +pub enum CodingRate { + _4_5 = 0x01, + _4_6 = 0x02, + _4_7 = 0x03, + _4_8 = 0x04, +} + +impl CodingRate { + pub fn value(self) -> u8 { + self as u8 + } +} + +impl From for CodingRate { + fn from(cr: device::CodingRate) -> Self { + match cr { + device::CodingRate::_4_5 => CodingRate::_4_5, + device::CodingRate::_4_6 => CodingRate::_4_6, + device::CodingRate::_4_7 => CodingRate::_4_7, + device::CodingRate::_4_8 => CodingRate::_4_8, + } + } +} + +#[derive(Clone, Copy)] +pub struct ModulationParams { + pub spreading_factor: SpreadingFactor, + pub bandwidth: Bandwidth, + pub coding_rate: CodingRate, + pub low_data_rate_optimize: u8, +} + +#[derive(Clone, Copy)] +pub struct PacketParams { + pub preamble_length: u16, // number of LoRa symbols in the preamble + pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length) + pub payload_length: u8, + pub crc_on: bool, + pub iq_inverted: bool, +} + +#[derive(Clone, Copy)] +pub enum CADSymbols { + _1 = 0x00, + _2 = 0x01, + _4 = 0x02, + _8 = 0x03, + _16 = 0x04, +} + +impl CADSymbols { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum CADExitMode { + CADOnly = 0x00, + CADRx = 0x01, + CADLBT = 0x10, +} + +impl CADExitMode { + pub fn value(self) -> u8 { + self as u8 + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs new file mode 100644 index 00000000..02a0a72e --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -0,0 +1,688 @@ +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +use super::mod_params::*; +use super::LoRa; + +// Internal frequency of the radio +const SX126X_XTAL_FREQ: u32 = 32000000; + +// Scaling factor used to perform fixed-point operations +const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; + +// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT +const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); + +// Maximum value for parameter symbNum +const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; + +// Provides board-specific functionality for Semtech SX126x-based boards + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + // Initialize the radio driver + pub(super) async fn sub_init(&mut self) -> Result<(), RadioError> { + self.brd_reset().await?; + self.brd_wakeup().await?; + self.sub_set_standby(StandbyMode::RC).await?; + self.brd_io_tcxo_init().await?; + self.brd_io_rf_switch_init().await?; + self.image_calibrated = false; + Ok(()) + } + + // Wakeup the radio if it is in Sleep mode and check that Busy is low + pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError> { + let operating_mode = self.brd_get_operating_mode(); + if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { + self.brd_wakeup().await?; + self.brd_ant_sw_on().await?; + } + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Save the payload to be sent in the radio buffer + pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError> { + self.brd_write_buffer(0x00, payload).await?; + Ok(()) + } + + // Read the payload received. + pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result> { + let (size, offset) = self.sub_get_rx_buffer_status().await?; + if (size as usize) > buffer.len() { + Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) + } else { + self.brd_read_buffer(offset, buffer).await?; + Ok(size) + } + } + + // Send a payload + pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError> { + self.sub_set_payload(payload).await?; + self.sub_set_tx(timeout).await?; + Ok(()) + } + + // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command. + // + // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of + // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts + // generated during this process will not cause undesired side-effects in the software. + // + // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. + pub(super) async fn sub_get_random(&mut self) -> Result> { + let mut reg_ana_lna_buffer_original = [0x00u8]; + let mut reg_ana_mixer_buffer_original = [0x00u8]; + let mut reg_ana_lna_buffer = [0x00u8]; + let mut reg_ana_mixer_buffer = [0x00u8]; + let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; + + self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) + .await?; + reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); + self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; + + self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) + .await?; + reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); + self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) + .await?; + + // Set radio in continuous reception + self.sub_set_rx(0xFFFFFFu32).await?; + + self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) + .await?; + + self.sub_set_standby(StandbyMode::RC).await?; + + self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) + .await?; + self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) + .await?; + + Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) + } + + // Set the radio in sleep mode + pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { + self.brd_ant_sw_off().await?; + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_low(); + + if !sleep_config.warm_start { + self.image_calibrated = false; + } + + self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) + .await?; + self.brd_set_operating_mode(RadioMode::Sleep); + Ok(()) + } + + // Set the radio in configuration mode + pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; + if mode == StandbyMode::RC { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } else { + self.brd_set_operating_mode(RadioMode::StandbyXOSC); + } + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_low(); + Ok(()) + } + + // Set the radio in FS mode + pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError> { + // antenna settings ??? + self.brd_write_command(OpCode::SetFS, &[]).await?; + self.brd_set_operating_mode(RadioMode::FrequencySynthesis); + Ok(()) + } + + // Set the radio in transmission mode with timeout specified + pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_set_operating_mode(RadioMode::Transmit); + self.brd_write_command(OpCode::SetTx, &buffer).await?; + Ok(()) + } + + // Set the radio in reception mode with timeout specified + pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_set_operating_mode(RadioMode::Receive); + self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; + self.brd_write_command(OpCode::SetRx, &buffer).await?; + Ok(()) + } + + // Set the radio in reception mode with Boosted LNA gain and timeout specified + pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_set_operating_mode(RadioMode::Receive); + // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity + self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; + self.brd_write_command(OpCode::SetRx, &buffer).await?; + Ok(()) + } + + // Set the Rx duty cycle management parameters + pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { + let buffer = [ + ((rx_time >> 16) & 0xFF) as u8, + ((rx_time >> 8) & 0xFF) as u8, + (rx_time & 0xFF) as u8, + ((sleep_time >> 16) & 0xFF) as u8, + ((sleep_time >> 8) & 0xFF) as u8, + (sleep_time & 0xFF) as u8, + ]; + + // antenna settings ??? + + self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; + self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); + Ok(()) + } + + // Set the radio in CAD mode + pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_write_command(OpCode::SetCAD, &[]).await?; + self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); + Ok(()) + } + + // Set the radio in continuous wave transmission mode + pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; + self.brd_set_operating_mode(RadioMode::Transmit); + Ok(()) + } + + // Set the radio in continuous preamble transmission mode + pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; + self.brd_set_operating_mode(RadioMode::Transmit); + Ok(()) + } + + // Decide which interrupt will stop the internal radio rx timer. + // false timer stop after header/syncword detection + // true timer stop after preamble detection + pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( + &mut self, + enable: bool, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) + .await?; + Ok(()) + } + + // Set the number of symbols the radio will wait to validate a reception + pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError> { + let mut exp = 0u8; + let mut reg; + let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; + while mant > 31 { + mant = (mant + 3) >> 2; + exp += 1; + } + reg = mant << ((2 * exp) + 1); + + self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; + + if symb_num != 0 { + reg = exp + (mant << 3); + self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; + } + + Ok(()) + } + + // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled + pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) + .await?; + Ok(()) + } + + // Calibrate the given radio block + pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError> { + self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) + .await?; + Ok(()) + } + + // Calibrate the image rejection based on the given frequency + pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError> { + let mut cal_freq = [0x00u8, 0x00u8]; + + if freq > 900000000 { + cal_freq[0] = 0xE1; + cal_freq[1] = 0xE9; + } else if freq > 850000000 { + cal_freq[0] = 0xD7; + cal_freq[1] = 0xDB; + } else if freq > 770000000 { + cal_freq[0] = 0xC1; + cal_freq[1] = 0xC5; + } else if freq > 460000000 { + cal_freq[0] = 0x75; + cal_freq[1] = 0x81; + } else if freq > 425000000 { + cal_freq[0] = 0x6B; + cal_freq[1] = 0x6F; + } + self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; + Ok(()) + } + + // Activate the extention of the timeout when a long preamble is used + pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Set the transmission parameters + // hp_max 0 for sx1261, 7 for sx1262 + // device_sel 1 for sx1261, 0 for sx1262 + // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT + pub(super) async fn sub_set_pa_config( + &mut self, + pa_duty_cycle: u8, + hp_max: u8, + device_sel: u8, + pa_lut: u8, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) + .await?; + Ok(()) + } + + // Define into which mode the chip goes after a TX / RX done + pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) + .await?; + Ok(()) + } + + // Set the IRQ mask and DIO masks + pub(super) async fn sub_set_dio_irq_params( + &mut self, + irq_mask: u16, + dio1_mask: u16, + dio2_mask: u16, + dio3_mask: u16, + ) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 8]; + + buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; + buffer[1] = (irq_mask & 0x00FF) as u8; + buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; + buffer[3] = (dio1_mask & 0x00FF) as u8; + buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; + buffer[5] = (dio2_mask & 0x00FF) as u8; + buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; + buffer[7] = (dio3_mask & 0x00FF) as u8; + self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; + Ok(()) + } + + // Return the current IRQ status + pub(super) async fn sub_get_irq_status(&mut self) -> Result> { + let mut irq_status = [0x00u8, 0x00u8]; + self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; + Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) + } + + // Indicate if DIO2 is used to control an RF Switch + pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; + Ok(()) + } + + // Indicate if the radio main clock is supplied from a TCXO + // tcxo_voltage voltage used to control the TCXO on/off from DIO3 + // timeout duration given to the TCXO to go to 32MHz + pub(super) async fn sub_set_dio3_as_tcxo_ctrl( + &mut self, + tcxo_voltage: TcxoCtrlVoltage, + timeout: u32, + ) -> Result<(), RadioError> { + let buffer = [ + tcxo_voltage.value() & 0x07, + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; + + Ok(()) + } + + // Set the RF frequency (Hz) + pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 4]; + + if !self.image_calibrated { + self.sub_calibrate_image(frequency).await?; + self.image_calibrated = true; + } + + let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); + + buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; + buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; + buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; + buffer[3] = (freq_in_pll_steps & 0xFF) as u8; + self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; + Ok(()) + } + + // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters. + pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError> { + self.packet_type = packet_type; + self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) + .await?; + Ok(()) + } + + // Get the current radio protocol (LoRa or GFSK) + pub(super) fn sub_get_packet_type(&mut self) -> PacketType { + self.packet_type + } + + // Set the transmission parameters + // power RF output power [-18..13] dBm + // ramp_time transmission ramp up time + pub(super) async fn sub_set_tx_params( + &mut self, + mut power: i8, + ramp_time: RampTime, + ) -> Result<(), RadioError> { + if self.brd_get_radio_type() == RadioType::SX1261 { + if power == 15 { + self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; + } else { + self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; + } + + if power >= 14 { + power = 14; + } else if power < -17 { + power = -17; + } + } else { + // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) + let mut tx_clamp_cfg = [0x00u8]; + self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; + tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); + self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; + + self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; + + if power > 22 { + power = 22; + } else if power < -9 { + power = -9; + } + } + + // power conversion of negative number from i8 to u8 ??? + self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) + .await?; + Ok(()) + } + + // Set the modulation parameters + pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError> { + if self.modulation_params.is_some() { + let mut buffer = [0x00u8; 4]; + + // Since this driver only supports LoRa, ensure the packet type is set accordingly + self.sub_set_packet_type(PacketType::LoRa).await?; + + let modulation_params = self.modulation_params.unwrap(); + buffer[0] = modulation_params.spreading_factor.value(); + buffer[1] = modulation_params.bandwidth.value(); + buffer[2] = modulation_params.coding_rate.value(); + buffer[3] = modulation_params.low_data_rate_optimize; + + self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; + Ok(()) + } else { + Err(RadioError::ModulationParamsMissing) + } + } + + // Set the packet parameters + pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError> { + if self.packet_params.is_some() { + let mut buffer = [0x00u8; 6]; + + // Since this driver only supports LoRa, ensure the packet type is set accordingly + self.sub_set_packet_type(PacketType::LoRa).await?; + + let packet_params = self.packet_params.unwrap(); + buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; + buffer[1] = (packet_params.preamble_length & 0xFF) as u8; + buffer[2] = packet_params.implicit_header as u8; + buffer[3] = packet_params.payload_length; + buffer[4] = packet_params.crc_on as u8; + buffer[5] = packet_params.iq_inverted as u8; + + self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + // Set the channel activity detection (CAD) parameters + // symbols number of symbols to use for CAD operations + // det_peak limit for detection of SNR peak used in the CAD + // det_min minimum symbol recognition for CAD + // exit_mode operation to be done at the end of CAD action + // timeout timeout value to abort the CAD activity + + pub(super) async fn sub_set_cad_params( + &mut self, + symbols: CADSymbols, + det_peak: u8, + det_min: u8, + exit_mode: CADExitMode, + timeout: u32, + ) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 7]; + + buffer[0] = symbols.value(); + buffer[1] = det_peak; + buffer[2] = det_min; + buffer[3] = exit_mode.value(); + buffer[4] = Self::timeout_1(timeout); + buffer[5] = Self::timeout_2(timeout); + buffer[6] = Self::timeout_3(timeout); + + self.brd_write_command(OpCode::SetCADParams, &buffer).await?; + self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); + Ok(()) + } + + // Set the data buffer base address for transmission and reception + pub(super) async fn sub_set_buffer_base_address( + &mut self, + tx_base_address: u8, + rx_base_address: u8, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) + .await?; + Ok(()) + } + + // Get the current radio status + pub(super) async fn sub_get_status(&mut self) -> Result> { + let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; + Ok(RadioStatus { + cmd_status: (status & (0x07 << 1)) >> 1, + chip_mode: (status & (0x07 << 4)) >> 4, + }) + } + + // Get the instantaneous RSSI value for the last packet received + pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { + let mut buffer = [0x00u8]; + self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; + let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? + Ok(rssi) + } + + // Get the last received packet buffer status + pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError> { + if self.packet_params.is_some() { + let mut status = [0x00u8; 2]; + let mut payload_length_buffer = [0x00u8]; + + self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; + if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { + self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) + .await?; + } else { + payload_length_buffer[0] = status[0]; + } + + let payload_length = payload_length_buffer[0]; + let offset = status[1]; + + Ok((payload_length, offset)) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + // Get the last received packet payload status + pub(super) async fn sub_get_packet_status(&mut self) -> Result> { + let mut status = [0x00u8; 3]; + self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; + + // check this ??? + let rssi = (-(status[0] as i8)) >> 1; + let snr = ((status[1] as i8) + 2) >> 2; + let signal_rssi = (-(status[2] as i8)) >> 1; + let freq_error = self.frequency_error; + + Ok(PacketStatus { + rssi, + snr, + signal_rssi, + freq_error, + }) + } + + // Get the possible system errors + pub(super) async fn sub_get_device_errors(&mut self) -> Result> { + let mut errors = [0x00u8; 2]; + self.brd_read_command(OpCode::GetErrors, &mut errors).await?; + + Ok(RadioSystemError { + rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, + rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, + pll_calibration: (errors[1] & (1 << 2)) != 0, + adc_calibration: (errors[1] & (1 << 3)) != 0, + image_calibration: (errors[1] & (1 << 4)) != 0, + xosc_start: (errors[1] & (1 << 5)) != 0, + pll_lock: (errors[1] & (1 << 6)) != 0, + pa_ramp: (errors[0] & (1 << 0)) != 0, + }) + } + + // Clear all the errors in the device + pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError> { + self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; + Ok(()) + } + + // Clear the IRQs + pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError> { + let mut buffer = [0x00u8, 0x00u8]; + buffer[0] = ((irq >> 8) & 0xFF) as u8; + buffer[1] = (irq & 0xFF) as u8; + self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; + Ok(()) + } + + // Utility functions + + fn timeout_1(timeout: u32) -> u8 { + ((timeout >> 16) & 0xFF) as u8 + } + fn timeout_2(timeout: u32) -> u8 { + ((timeout >> 8) & 0xFF) as u8 + } + fn timeout_3(timeout: u32) -> u8 { + (timeout & 0xFF) as u8 + } + + // check this ??? + fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { + let b0 = buffer[0] as u32; + let b1 = buffer[1] as u32; + let b2 = buffer[2] as u32; + let b3 = buffer[3] as u32; + (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 + } + + fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { + // Get integer and fractional parts of the frequency computed with a PLL step scaled value + let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; + let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); + + (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) + + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) + } +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a5d340c6..54df16ba 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,8 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "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", + "embassy-lora", "lorawan-device", "lorawan"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -16,6 +17,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm 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 } embedded-io = "0.3.0" +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "time", "defmt"], optional = true } + +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs new file mode 100644 index 00000000..46cb848b --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -0,0 +1,84 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +#![no_std] +#![no_main] +#![macro_use] +#![allow(dead_code)] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::sx126x::*; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{interrupt, spim}; +use embassy_time::{Duration, Timer}; +use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M1; // M16 ??? + + let mut radio = { + let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + + match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { + Ok(r) => r, + Err(err) => { + info!("Sx126xRadio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + let rf_config = RfConfig { + frequency: 903900000, // channel in Hz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }; + + let mut buffer = [00u8; 100]; + + // P2P receive + match radio.rx(rf_config, &mut buffer).await { + Ok((buffer_len, rx_quality)) => info!( + "RX received = {:?} with length = {} rssi = {} snr = {}", + &buffer[0..buffer_len], + buffer_len, + rx_quality.rssi(), + rx_quality.snr() + ), + Err(err) => info!("RX error = {}", err), + } + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); +} diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs new file mode 100644 index 00000000..57aaea66 --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -0,0 +1,173 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] +#![feature(alloc_error_handler)] +#![allow(incomplete_features)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::sx126x::*; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::temp::Temp; +use embassy_nrf::{interrupt, spim}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pubsub::{PubSubChannel, Publisher}; +use embassy_time::{Duration, Timer}; +use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; +use {defmt_rtt as _, panic_probe as _, panic_probe as _}; + +// Sensor packet constants +const TEMPERATURE_UID: u8 = 0x01; +const MOTION_UID: u8 = 0x02; + +// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) +static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); + +#[derive(Clone, defmt::Format)] +enum Message { + Temperature(i32), + MotionDetected, +} + +#[embassy_executor::task] +async fn temperature_task( + mut temperature: Temp<'static>, + publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, +) { + Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds + + let mut temperature_reporting_threshhold = 10; + + loop { + let value = temperature.read().await; + let mut temperature_val = value.to_num::(); + + info!("Temperature: {}", temperature_val); // debug ??? + + // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report + + if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 { + temperature_val += 1; + } + + if temperature_reporting_threshhold > temperature_val + && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5) + { + temperature_reporting_threshhold = temperature_val; + publisher.publish(Message::Temperature(temperature_val)).await; + } else if temperature_val <= 3 { + publisher.publish(Message::Temperature(temperature_val)).await; + } + + Timer::after(Duration::from_secs(20 * 60)).await; + } +} + +#[embassy_executor::task] +async fn motion_detection_task( + mut pir_pin: Input<'static, AnyPin>, + publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, +) { + Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds + + loop { + // wait for motion detection + pir_pin.wait_for_low().await; + publisher.publish(Message::MotionDetected).await; + + // wait a minute before setting up for more motion detection + Timer::after(Duration::from_secs(60)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + // set up to funnel temperature and motion detection events to the Lora Tx task + let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); + let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); + let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); + + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M1; // M16 ??? + + let mut radio = { + let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + + match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { + Ok(r) => r, + Err(err) => { + info!("Sx126xRadio error = {}", err); + return; + } + } + }; + + // set up for the temperature task + let temperature_irq = interrupt::take!(TEMP); + let temperature = Temp::new(p.TEMP, temperature_irq); + + // set the motion detection pin + let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up); + + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher))); + unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher))); + + loop { + let message = lora_tx_subscriber.next_message_pure().await; + + let tx_config = TxConfig { + // 11 byte maximum payload for Bandwidth 125 and SF 10 + pw: 20, // up to 20 // 5 ??? + rf: RfConfig { + frequency: 903900000, // channel in Hz, not MHz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }, + }; + + let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8]; + match message { + Message::Temperature(temperature) => buffer[1] = temperature as u8, + Message::MotionDetected => buffer[3] = 0x01u8, + }; + + // crypto for text ??? + match radio.tx(tx_config, &buffer).await { + Ok(ret_val) => info!("TX ret_val = {}", ret_val), + Err(err) => info!("TX error = {}", err), + } + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } +} From 526e90d3f3b986fedc55ec421178b7b2ec954abf Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 28 Sep 2022 14:27:34 -0500 Subject: [PATCH 020/374] Update some outstanding questions --- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 18 +++++++++--------- examples/nrf/src/bin/lora_p2p_sense.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs index 02f5f83a..53fbde74 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -511,22 +511,23 @@ where cad_activity_detected: Option<&mut bool>, ) -> Result<(), RadioError> { loop { - info!("process_irq loop entered"); // debug ??? + trace!("process_irq loop entered"); let de = self.sub_get_device_errors().await?; - info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", + trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); let st = self.sub_get_status().await?; - info!( + trace!( "radio status: cmd_status: {:x}, chip_mode: {:x}", - st.cmd_status, st.chip_mode + st.cmd_status, + st.chip_mode ); self.dio1.wait_for_high().await.map_err(|_| DIO1)?; let operating_mode = self.brd_get_operating_mode(); let irq_flags = self.sub_get_irq_status().await?; self.sub_clear_irq_status(irq_flags).await?; - info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? + trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // check for errors and unexpected interrupt masks (based on operation mode) if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { @@ -568,13 +569,12 @@ where return Err(RadioError::CADUnexpected); } - // debug ??? if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { - info!("HeaderValid"); + trace!("HeaderValid"); } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { - info!("PreambleDetected"); + trace!("PreambleDetected"); } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { - info!("SyncwordValid"); + trace!("SyncwordValid"); } // handle completions diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 57aaea66..3c6bb876 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -44,7 +44,7 @@ async fn temperature_task( let value = temperature.read().await; let mut temperature_val = value.to_num::(); - info!("Temperature: {}", temperature_val); // debug ??? + info!("Temperature: {}", temperature_val); // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report From f9c62d4f1da1c4a38219056be3f658c34d0031af Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 09:09:35 +0200 Subject: [PATCH 021/374] Add flowcontrol to UART --- embassy-stm32/src/usart/mod.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6c266874..22de6d18 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -214,7 +214,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.cr3().write(|_w| {}); r.brr().write_value(regs::Brr(div)); r.cr1().write(|w| { w.set_ue(true); @@ -241,6 +240,29 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } + pub fn new_with_rtscts( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts, rts); + + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + Self::new(_inner, rx, tx, tx_dma, rx_dma, config) + } + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> where TxDma: crate::usart::TxDma, From 90d392205fe7c07cbc59b09679cde6cfc1e244b6 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 10:00:30 -0700 Subject: [PATCH 022/374] embassy-rp: inline I2c::regs It just returns a literal constant, so there's no reason not to always inline it. --- embassy-rp/src/i2c.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 52f910ce..ab56c935 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -499,6 +499,7 @@ macro_rules! impl_instance { type Interrupt = crate::interrupt::$irq; + #[inline] fn regs() -> pac::i2c::I2c { pac::$type } From 8d38eacae426ef1b1e1f7255eed50a9588793fb4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 27 Sep 2022 23:24:22 -0700 Subject: [PATCH 023/374] rp i2c: remove vestiges of DMA --- embassy-rp/src/i2c.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index ab56c935..a6f27882 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; -use crate::dma::AnyChannel; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -52,9 +51,6 @@ 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)>, } @@ -66,7 +62,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(scl, sda); - Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + Self::new_inner(_peri, scl.map_into(), sda.map_into(), config) } } @@ -75,8 +71,6 @@ 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>, config: Config, ) -> Self { into_ref!(_peri); @@ -173,9 +167,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } Self { - _tx_dma, - _rx_dma, - _dma_buf: [0; 256], phantom: PhantomData, } } From 72b645b0c96c7b8312d0a64f851e807faacd78af Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 27 Sep 2022 23:44:14 -0700 Subject: [PATCH 024/374] rp i2c: make blocking only for Mode=Blocking --- embassy-rp/src/i2c.rs | 212 +++++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 105 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index a6f27882..c609b02e 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -56,14 +56,115 @@ pub struct I2c<'d, T: Instance, M: Mode> { impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( - _peri: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(scl, sda); - Self::new_inner(_peri, scl.map_into(), sda.map_into(), config) + Self::new_inner(peri, scl.map_into(), sda.map_into(), config) } + + 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 p.ic_txflr().read().txflr() == FIFO_SIZE {} + + p.ic_data_cmd().write(|w| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while p.ic_rxflr().read().rxflr() == 0 { + self.read_and_clear_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| { + w.set_stop(send_stop && last); + 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_err() || (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. + 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, true, 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, M: Mode> I2c<'d, T, M> { @@ -217,111 +318,12 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } - 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 p.ic_txflr().read().txflr() == FIFO_SIZE {} - - p.ic_data_cmd().write(|w| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); - - w.set_cmd(true); - }); - - while p.ic_rxflr().read().rxflr() == 0 { - self.read_and_clear_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| { - w.set_stop(send_stop && last); - 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_err() || (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. - 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, true, 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 - } } mod eh02 { use super::*; - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, Blocking> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -329,7 +331,7 @@ mod eh02 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, Blocking> { type Error = Error; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -337,7 +339,7 @@ mod eh02 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, Blocking> { type Error = Error; fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -370,7 +372,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, Blocking> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From 5e2c52ee5b6fcc5b50589fd2590657e3f1083bff Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 01:20:04 -0700 Subject: [PATCH 025/374] embassy-rp: async i2c implementation This is an interrupt-driven async i2c master implementation. It makes as best use of the RP2040's i2c block's fifos as possible to minimize interrupts. It implements embedded_hal_async::i2c for easy interop. WIP async impl --- embassy-rp/src/i2c.rs | 376 +++++++++++++++++++++++++++++++++++++++-- examples/rp/Cargo.toml | 3 + 2 files changed, 369 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index c609b02e..6fc64d84 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,6 +1,10 @@ +use core::future; use core::marker::PhantomData; +use core::task::Poll; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; use crate::gpio::sealed::Pin; @@ -79,7 +83,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // NOTE(unsafe) We have &mut self unsafe { // wait until there is space in the FIFO to write the next byte - while p.ic_txflr().read().txflr() == FIFO_SIZE {} + while Self::tx_fifo_full() {} p.ic_data_cmd().write(|w| { w.set_restart(restart && first); @@ -88,7 +92,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { w.set_cmd(true); }); - while p.ic_rxflr().read().rxflr() == 0 { + while Self::rx_fifo_len() == 0 { self.read_and_clear_abort_reason()?; } @@ -165,9 +169,250 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // Automatic Stop } } + +static I2C_WAKER: AtomicWaker = AtomicWaker::new(); + +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new_async( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq); + + let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); + + irq.set_handler(Self::on_interrupt); + unsafe { + let i2c = T::regs(); + + // mask everything initially + i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + } + irq.unpend(); + debug_assert!(!irq.is_pending()); + irq.enable(); + + i2c + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + async fn wait_on(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + future::poll_fn(|cx| { + let r = f(self); + + if r.is_pending() { + I2C_WAKER.register(cx.waker()); + g(self); + } + r + }) + .await + } + + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt(_: *mut ()) { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + I2C_WAKER.wake(); + } + + async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + + let mut remaining = buffer.len(); + let mut remaining_queue = buffer.len(); + + let mut abort_reason = None; + + while remaining > 0 { + // Waggle SCK - basically the same as write + let tx_fifo_space = Self::tx_fifo_capacity(); + let mut batch = 0; + + debug_assert!(remaining_queue > 0); + + for _ in 0..remaining_queue.min(tx_fifo_space as usize) { + remaining_queue -= 1; + let last = remaining_queue == 0; + batch += 1; + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_restart(restart && remaining_queue == buffer.len() - 1); + w.set_stop(last && send_stop); + w.set_cmd(true); + }); + } + } + + // We've either run out of txfifo or just plain finished setting up + // the clocks for the message - either way we need to wait for rx + // data. + + debug_assert!(batch > 0); + let res = self + .wait_on( + |me| { + let rxfifo = Self::rx_fifo_len(); + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if rxfifo >= batch { + Poll::Ready(Ok(rxfifo)) + } else { + Poll::Pending + } + }, + |_me| unsafe { + // Set the read threshold to the number of bytes we're + // expecting so we don't get spurious interrupts. + p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1)); + + p.ic_intr_mask().modify(|w| { + w.set_m_rx_full(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + match res { + Err(reason) => { + abort_reason = Some(reason); + // XXX keep going anyway? + break; + } + Ok(rxfifo) => { + // Fetch things from rx fifo. We're assuming we're the only + // rxfifo reader, so nothing else can take things from it. + let rxbytes = (rxfifo as usize).min(remaining); + let received = buffer.len() - remaining; + for b in &mut buffer[received..received + rxbytes] { + *b = unsafe { p.ic_data_cmd().read().dat() }; + } + remaining -= rxbytes; + } + }; + } + + // wait for stop condition to be emitted. + self.wait_on( + |_me| unsafe { + if !p.ic_raw_intr_stat().read().stop_det() && send_stop { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { p.ic_clr_stop_det().read() }; + + if let Some(abort_reason) = abort_reason { + return Err(abort_reason); + } + Ok(()) + } + + async fn write_async_internal( + &mut self, + bytes: impl IntoIterator, + send_stop: bool, + ) -> Result<(), Error> { + let p = T::regs(); + + let mut bytes = bytes.into_iter().peekable(); + + 'xmit: loop { + let tx_fifo_space = Self::tx_fifo_capacity(); + + for _ in 0..tx_fifo_space { + if let Some(byte) = bytes.next() { + let last = bytes.peek().is_none(); + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(last && send_stop); + w.set_cmd(false); + w.set_dat(byte); + }); + } + } else { + break 'xmit; + } + } + + self.wait_on( + |me| { + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if !Self::tx_fifo_full() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await?; + } + + // wait for fifo to drain + self.wait_on( + |_me| unsafe { + if p.ic_raw_intr_stat().read().tx_empty() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + Ok(()) + } + + pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + + pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.write_async_internal(buffer.iter().copied(), true).await + } } -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { +impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { fn new_inner( _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, @@ -182,6 +427,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let p = T::regs(); unsafe { + let reset = T::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + p.ic_enable().write(|w| w.set_enable(false)); // Select controller mode & speed @@ -267,9 +516,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { - phantom: PhantomData, - } + Self { phantom: PhantomData } } fn setup(addr: u16) -> Result<(), Error> { @@ -290,6 +537,23 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } + #[inline] + fn tx_fifo_full() -> bool { + Self::tx_fifo_capacity() == 0 + } + + #[inline] + fn tx_fifo_capacity() -> u8 { + let p = T::regs(); + unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() } + } + + #[inline] + fn rx_fifo_len() -> u8 { + let p = T::regs(); + unsafe { p.ic_rxflr().read().rxflr() } + } + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); unsafe { @@ -317,7 +581,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } } - } mod eh02 { @@ -444,6 +707,91 @@ mod eh1 { } } } +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod nightly { + use core::future::Future; + + use embedded_hal_1::i2c::Operation; + use embedded_hal_async::i2c::AddressMode; + + use super::*; + + impl<'d, A, T> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> + where + A: AddressMode + Into + 'static, + T: Instance + 'd, + { + type ReadFuture<'a> = impl Future> + 'a + where Self: 'a; + type WriteFuture<'a> = impl Future> + 'a + where Self: 'a; + type WriteReadFuture<'a> = impl Future> + 'a + where Self: 'a; + type TransactionFuture<'a, 'b> = impl Future> + 'a + where Self: 'a, 'b: 'a; + + fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + } + + fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.write_async_internal(write.iter().copied(), true).await + } + } + + fn write_read<'a>( + &'a mut self, + address: A, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.write_async_internal(bytes.iter().cloned(), false).await?; + self.read_async_internal(buffer, false, true).await + } + } + + fn transaction<'a, 'b>( + &'a mut self, + address: A, + operations: &'a mut [Operation<'b>], + ) -> Self::TransactionFuture<'a, 'b> { + let addr: u16 = address.into(); + + async move { + let mut iterator = operations.iter_mut(); + + while let Some(op) = iterator.next() { + let last = iterator.len() == 0; + + match op { + Operation::Read(buffer) => { + Self::setup(addr)?; + self.read_async_internal(buffer, false, last).await?; + } + Operation::Write(buffer) => { + Self::setup(addr)?; + self.write_async_internal(buffer.into_iter().cloned(), last).await?; + } + } + } + Ok(()) + } + } + } +} fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 @@ -459,6 +807,7 @@ mod sealed { type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; + fn reset() -> crate::pac::resets::regs::Peripherals; } pub trait Mode {} @@ -485,7 +834,7 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $irq:ident, $reset: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; @@ -496,13 +845,20 @@ macro_rules! impl_instance { fn regs() -> pac::i2c::I2c { pac::$type } + + #[inline] + fn reset() -> pac::resets::regs::Peripherals { + let mut ret = pac::resets::regs::Peripherals::default(); + ret.$reset(true); + ret + } } impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, I2C0_IRQ, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, 34, 35); +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 3c8f923e..e689df6b 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -30,3 +30,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } static_cell = "1.0.0" + +[profile.release] +debug = true From 1ee4bb22deb19e93a7c68e04875889e3e4e31c29 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 09:35:19 -0700 Subject: [PATCH 026/374] embassy-rp i2c: async (non-blocking) example Simple example exercising an mcp23017 GPIO expander, configured on RP2040 GPIOs 14+15 (i2c1) with 8 inputs and 8 outputs. Input bit 0 controls whether to display a mcp23017 register dump. --- examples/rp/src/bin/i2c_async.rs | 102 +++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 examples/rp/src/bin/i2c_async.rs diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs new file mode 100644 index 00000000..d1a2e3cd --- /dev/null +++ b/examples/rp/src/bin/i2c_async.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::i2c::{self, Config}; +use embassy_rp::interrupt; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _}; + +#[allow(dead_code)] +mod mcp23017 { + pub const ADDR: u8 = 0x20; // default addr + + macro_rules! mcpregs { + ($($name:ident : $val:expr),* $(,)?) => { + $( + pub const $name: u8 = $val; + )* + + pub fn regname(reg: u8) -> &'static str { + match reg { + $( + $val => stringify!($name), + )* + _ => panic!("bad reg"), + } + } + } + } + + // These are correct for IOCON.BANK=0 + mcpregs! { + IODIRA: 0x00, + IPOLA: 0x02, + GPINTENA: 0x04, + DEFVALA: 0x06, + INTCONA: 0x08, + IOCONA: 0x0A, + GPPUA: 0x0C, + INTFA: 0x0E, + INTCAPA: 0x10, + GPIOA: 0x12, + OLATA: 0x14, + IODIRB: 0x01, + IPOLB: 0x03, + GPINTENB: 0x05, + DEFVALB: 0x07, + INTCONB: 0x09, + IOCONB: 0x0B, + GPPUB: 0x0D, + INTFB: 0x0F, + INTCAPB: 0x11, + GPIOB: 0x13, + OLATB: 0x15, + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let sda = p.PIN_14; + let scl = p.PIN_15; + let irq = interrupt::take!(I2C1_IRQ); + + info!("set up i2c "); + let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default()); + + use mcp23017::*; + + info!("init mcp23017 config for IxpandO"); + // init - a outputs, b inputs + i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap(); + i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap(); + i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups + + let mut val = 1; + loop { + let mut portb = [0]; + + i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap(); + info!("portb = {:02x}", portb[0]); + i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap(); + val = val.rotate_left(1); + + // get a register dump + info!("getting register dump"); + let mut regs = [0; 22]; + i2c.write_read(ADDR, &[0], &mut regs).await.unwrap(); + // always get the regdump but only display it if portb'0 is set + if portb[0] & 1 != 0 { + for (idx, reg) in regs.into_iter().enumerate() { + info!("{} => {:02x}", regname(idx as u8), reg); + } + } + + Timer::after(Duration::from_millis(100)).await; + } +} From 09afece93d0dccb750a0dbc9c63282d3dca55e48 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 19:28:27 -0700 Subject: [PATCH 027/374] make I2c::write_async take an iterator There's no other iterator async API right now. --- embassy-rp/src/i2c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 6fc64d84..f62cf0b8 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -406,9 +406,9 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.read_async_internal(buffer, false, true).await } - pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> { + pub async fn write_async(&mut self, addr: u16, bytes : impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; - self.write_async_internal(buffer.iter().copied(), true).await + self.write_async_internal(bytes, true).await } } From e8bb8faa23c1e8b78285646ca7e711bafe990e20 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 2 Oct 2022 15:08:58 -0700 Subject: [PATCH 028/374] rp i2c: allow blocking ops on async contexts --- embassy-rp/src/i2c.rs | 210 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f62cf0b8..e01692dc 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -68,106 +68,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { into_ref!(scl, sda); Self::new_inner(peri, scl.map_into(), sda.map_into(), config) } - - 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| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); - - w.set_cmd(true); - }); - - while Self::rx_fifo_len() == 0 { - self.read_and_clear_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| { - w.set_stop(send_stop && last); - 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_err() || (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. - 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, true, 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 - } } static I2C_WAKER: AtomicWaker = AtomicWaker::new(); @@ -406,7 +306,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.read_async_internal(buffer, false, true).await } - pub async fn write_async(&mut self, addr: u16, bytes : impl IntoIterator) -> Result<(), Error> { + pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; self.write_async_internal(bytes, true).await } @@ -581,12 +481,112 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { } } } + + 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| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while Self::rx_fifo_len() == 0 { + self.read_and_clear_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| { + w.set_stop(send_stop && last); + 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_err() || (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. + 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, true, 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 + } } mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, Blocking> { + 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> { @@ -594,7 +594,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, Blocking> { + 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> { @@ -602,7 +602,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, Blocking> { + 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> { @@ -635,7 +635,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From cae84991790976387aa4d4b7afae90094a876b25 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 3 Oct 2022 01:00:03 -0700 Subject: [PATCH 029/374] rp i2c: clean up tx_abrt handling Make sure we always wait for the stop bit if there's a reason to - either because we sent one, or because there was a hardware tx abort. --- embassy-rp/src/i2c.rs | 136 ++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index e01692dc..f004c522 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -135,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let mut remaining = buffer.len(); let mut remaining_queue = buffer.len(); - let mut abort_reason = None; + let mut abort_reason = Ok(()); while remaining > 0 { // Waggle SCK - basically the same as write @@ -190,8 +190,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { match res { Err(reason) => { - abort_reason = Some(reason); - // XXX keep going anyway? + abort_reason = Err(reason); break; } Ok(rxfifo) => { @@ -207,29 +206,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }; } - // wait for stop condition to be emitted. - self.wait_on( - |_me| unsafe { - if !p.ic_raw_intr_stat().read().stop_det() && send_stop { - Poll::Pending - } else { - Poll::Ready(()) - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_tx_abrt(true); - }); - }, - ) - .await; - unsafe { p.ic_clr_stop_det().read() }; - - if let Some(abort_reason) = abort_reason { - return Err(abort_reason); - } - Ok(()) + self.wait_stop_det(abort_reason, send_stop).await } async fn write_async_internal( @@ -241,7 +218,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let mut bytes = bytes.into_iter().peekable(); - 'xmit: loop { + let res = 'xmit: loop { let tx_fifo_space = Self::tx_fifo_capacity(); for _ in 0..tx_fifo_space { @@ -256,49 +233,76 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }); } } else { - break 'xmit; + break 'xmit Ok(()); } } - self.wait_on( - |me| { - if let Err(abort_reason) = me.read_and_clear_abort_reason() { - Poll::Ready(Err(abort_reason)) - } else if !Self::tx_fifo_full() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }) - }, - ) - .await?; + let res = self + .wait_on( + |me| { + if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { + Poll::Ready(abort_reason) + } else if !Self::tx_fifo_full() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await; + if res.is_err() { + break res; + } + }; + + self.wait_stop_det(res, send_stop).await + } + + /// Helper to wait for a stop bit, for both tx and rx. If we had an abort, + /// then we'll get a hardware-generated stop, otherwise wait for a stop if + /// we're expecting it. + /// + /// Also handles an abort which arises while processing the tx fifo. + async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> { + if had_abort.is_err() || do_stop { + let p = T::regs(); + + let had_abort2 = self + .wait_on( + |me| unsafe { + // We could see an abort while processing fifo backlog, + // so handle it here. + let abort = me.read_and_clear_abort_reason(); + if had_abort.is_ok() && abort.is_err() { + Poll::Ready(abort) + } else if p.ic_raw_intr_stat().read().stop_det() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { + p.ic_clr_stop_det().read(); + } + + had_abort.and(had_abort2) + } else { + had_abort } - - // wait for fifo to drain - self.wait_on( - |_me| unsafe { - if p.ic_raw_intr_stat().read().tx_empty() { - Poll::Ready(()) - } else { - Poll::Pending - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }); - }, - ) - .await; - - Ok(()) } pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { From 4fd831e4a88fdba14751c8a71ae45fc0eac92c66 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 3 Oct 2022 18:50:03 -0700 Subject: [PATCH 030/374] rp async i2c: raise the tx_empty threshold Assert "tx_empty" interrupt a little early so there's time to wake up and start refilling the fifo before it drains. This avoids stalling the i2c bus if the tx fifo completely drains. --- embassy-rp/src/i2c.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f004c522..68cfc653 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -243,12 +243,18 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { Poll::Ready(abort_reason) } else if !Self::tx_fifo_full() { + // resume if there's any space free in the tx fifo Poll::Ready(Ok(())) } else { Poll::Pending } }, |_me| unsafe { + // Set tx "free" threshold a little high so that we get + // woken before the fifo completely drains to minimize + // transfer stalls. + p.ic_tx_tl().write(|w| w.set_tx_tl(1)); + p.ic_intr_mask().modify(|w| { w.set_m_tx_empty(true); w.set_m_tx_abrt(true); From f554962f54b9f9e8f85a0dd099635619bd1acd1d Mon Sep 17 00:00:00 2001 From: ceekdee Date: Sat, 8 Oct 2022 14:32:22 -0500 Subject: [PATCH 031/374] Improve generics and consolidate antenna handling --- embassy-lora/src/sx126x/mod.rs | 48 ++++++------------- .../src/sx126x/sx126x_lora/board_specific.rs | 39 ++++++++++----- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 19 ++++---- .../src/sx126x/sx126x_lora/subroutine.rs | 34 ++++--------- examples/nrf/src/bin/lora_p2p_report.rs | 8 ++-- examples/nrf/src/bin/lora_p2p_sense.rs | 8 ++-- 6 files changed, 69 insertions(+), 87 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index d67c7b10..ed8cb405 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -13,35 +13,29 @@ use sx126x_lora::LoRa; use self::sx126x_lora::mod_params::RadioError; /// Semtech Sx126x LoRa peripheral -pub struct Sx126xRadio +pub struct Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { - pub lora: LoRa, + pub lora: LoRa, } -impl Sx126xRadio +impl Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { pub async fn new( spi: SPI, - cs: CS, - reset: RESET, - antenna_rx: ANTRX, - antenna_tx: ANTTX, + cs: CTRL, + reset: CTRL, + antenna_rx: CTRL, + antenna_tx: CTRL, dio1: WAIT, busy: WAIT, enable_public_network: bool, @@ -53,13 +47,10 @@ where } } -impl Timings for Sx126xRadio +impl Timings for Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { @@ -71,13 +62,10 @@ where } } -impl PhyRxTx for Sx126xRadio +impl PhyRxTx for Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { @@ -86,10 +74,7 @@ where type TxFuture<'m> = impl Future> + 'm where SPI: 'm, - CS: 'm, - RESET: 'm, - ANTRX: 'm, - ANTTX: 'm, + CTRL: 'm, WAIT: 'm, BUS: 'm; @@ -122,10 +107,7 @@ where type RxFuture<'m> = impl Future> + 'm where SPI: 'm, - CS: 'm, - RESET: 'm, - ANTRX: 'm, - ANTTX: 'm, + CTRL: 'm, WAIT: 'm, BUS: 'm; diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs index 5b4891c0..1fb08588 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -13,13 +13,10 @@ const BRD_TCXO_WAKEUP_TIME: u32 = 10; // Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. // The base implementation supports the RAK4631 board. -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. @@ -210,14 +207,34 @@ where RadioType::SX1262 } - // Initialize the RF switch I/O pins interface - pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently + // Quiesce the antenna(s). + pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_low().map_err(|_| AntRx)?; + } + Ok(()) } - // De-initialize the RF switch I/O pins interface for MCU low power modes - pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently + // Prepare the antenna(s) for a receive operation + pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_high().map_err(|_| AntRx)?; + } + Ok(()) + } + + // Prepare the antenna(s) for a send operation + pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_rx.set_low().map_err(|_| AntRx)?; + self.antenna_tx.set_high().map_err(|_| AntTx)?; + } + Ok(()) } // Check if the given RF frequency is supported by the hardware diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs index 53fbde74..280f26d5 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -26,12 +26,12 @@ const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, const RADIO_WAKEUP_TIME: u32 = 3; /// Provides high-level access to Semtech SX126x-based boards -pub struct LoRa { +pub struct LoRa { spi: SPI, - cs: CS, - reset: RESET, - antenna_rx: ANTRX, - antenna_tx: ANTTX, + cs: CTRL, + reset: CTRL, + antenna_rx: CTRL, + antenna_tx: CTRL, dio1: WAIT, busy: WAIT, operating_mode: RadioMode, @@ -45,17 +45,14 @@ pub struct LoRa { frequency_error: u32, } -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () - pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { + pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self { Self { spi, cs, diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs index 02a0a72e..283e6099 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -19,13 +19,10 @@ const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; // Provides board-specific functionality for Semtech SX126x-based boards -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { // Initialize the radio driver @@ -44,7 +41,6 @@ where let operating_mode = self.brd_get_operating_mode(); if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { self.brd_wakeup().await?; - self.brd_ant_sw_on().await?; } self.brd_wait_on_busy().await?; Ok(()) @@ -117,10 +113,7 @@ where // Set the radio in sleep mode pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { - self.brd_ant_sw_off().await?; - - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_sleep()?; if !sleep_config.warm_start { self.image_calibrated = false; @@ -141,8 +134,7 @@ where self.brd_set_operating_mode(RadioMode::StandbyXOSC); } - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_sleep()?; Ok(()) } @@ -162,8 +154,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_set_operating_mode(RadioMode::Transmit); self.brd_write_command(OpCode::SetTx, &buffer).await?; @@ -178,8 +169,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_set_operating_mode(RadioMode::Receive); self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; @@ -195,8 +185,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_set_operating_mode(RadioMode::Receive); // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity @@ -225,8 +214,7 @@ where // Set the radio in CAD mode pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_write_command(OpCode::SetCAD, &[]).await?; self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); @@ -235,8 +223,7 @@ where // Set the radio in continuous wave transmission mode pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; self.brd_set_operating_mode(RadioMode::Transmit); @@ -245,8 +232,7 @@ where // Set the radio in continuous preamble transmission mode pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; self.brd_set_operating_mode(RadioMode::Transmit); diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs index 46cb848b..4ba3d30c 100644 --- a/examples/nrf/src/bin/lora_p2p_report.rs +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -24,12 +24,12 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); - let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { Ok(r) => r, diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 3c6bb876..405a8403 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -97,12 +97,12 @@ async fn main(spawner: Spawner) { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); - let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { Ok(r) => r, From e1faf8860776f6ad2bac2f3b06e7160fe00da7df Mon Sep 17 00:00:00 2001 From: huntc Date: Sun, 9 Oct 2022 13:07:25 +1100 Subject: [PATCH 032/374] Removes some of the code duplication for UarteWithIdle This commit removes some of the code duplication for UarteWithIdle at the expense of requiring a split. As the example illustrates though, this expense seems worth the benefit in terms of maintenance, and the avoidance of copying over methods. My main motivation for this commit was actually due to the `event_endtx` method not having been copied across. --- embassy-nrf/src/uarte.rs | 468 +++++++++++------------------- examples/nrf/src/bin/uart_idle.rs | 7 +- 2 files changed, 170 insertions(+), 305 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index d9959911..636d6c7a 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> { (self.tx, self.rx) } + /// Split the Uarte into a transmitter and receiver that will + /// return on idle, which is determined as the time it takes + /// for two bytes to be received. + pub fn split_with_idle( + self, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { + let mut timer = Timer::new(timer); + + into_ref!(ppi_ch1, ppi_ch2); + + let r = T::regs(); + + // BAUDRATE register values are `baudrate * 2^32 / 16000000` + // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values + // + // We want to stop RX if line is idle for 2 bytes worth of time + // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) + // This gives us the amount of 16M ticks for 20 bits. + let baudrate = r.baudrate.read().baudrate().variant().unwrap(); + let timeout = 0x8000_0000 / (baudrate as u32 / 40); + + timer.set_frequency(Frequency::F16MHz); + timer.cc(0).write(timeout); + timer.cc(0).short_compare_clear(); + timer.cc(0).short_compare_stop(); + + let mut ppi_ch1 = Ppi::new_one_to_two( + ppi_ch1.map_into(), + Event::from_reg(&r.events_rxdrdy), + timer.task_clear(), + timer.task_start(), + ); + ppi_ch1.enable(); + + let mut ppi_ch2 = Ppi::new_one_to_one( + ppi_ch2.map_into(), + timer.cc(0).event_compare(), + Task::from_reg(&r.tasks_stoprx), + ); + ppi_ch2.enable(); + + ( + self.tx, + UarteRxWithIdle { + rx: self.rx, + timer, + ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + }, + ) + } + /// Return the endtx event for use with PPI pub fn event_endtx(&self) -> Event { let r = T::regs(); @@ -597,6 +652,117 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { } } +pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { + rx: UarteRx<'d, T>, + timer: Timer<'d, U>, + ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, +} + +impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.ppi_ch1.disable(); + self.rx.read(buffer).await + } + + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.ppi_ch1.disable(); + self.rx.blocking_read(buffer) + } + + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let ptr = buffer.as_ptr(); + let len = buffer.len(); + + let r = T::regs(); + let s = T::state(); + + self.ppi_ch1.enable(); + + let drop = OnDrop::new(|| { + self.timer.stop(); + + r.intenclr.write(|w| w.endrx().clear()); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + }); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenset.write(|w| w.endrx().set()); + + compiler_fence(Ordering::SeqCst); + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + poll_fn(|cx| { + s.endrx_waker.register(cx.waker()); + if r.events_endrx.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + let n = r.rxd.amount.read().amount().bits() as usize; + + self.timer.stop(); + r.events_rxstarted.reset(); + + drop.defuse(); + + Ok(n) + } + + pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let ptr = buffer.as_ptr(); + let len = buffer.len(); + + let r = T::regs(); + + self.ppi_ch1.enable(); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenclr.write(|w| w.endrx().clear()); + + compiler_fence(Ordering::SeqCst); + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + + compiler_fence(Ordering::SeqCst); + let n = r.rxd.amount.read().amount().bits() as usize; + + self.timer.stop(); + r.events_rxstarted.reset(); + + Ok(n) + } +} + #[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { // Do nothing @@ -665,270 +831,6 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) { } } -/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels, -/// allowing it to implement the ReadUntilIdle trait. -pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { - tx: UarteTx<'d, U>, - rx: UarteRxWithIdle<'d, U, T>, -} - -impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { - /// Create a new UARTE without hardware flow control - pub fn new( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

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

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - cts: impl Peripheral

+ 'd, - rts: impl Peripheral

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

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: PeripheralRef<'d, AnyPin>, - txd: PeripheralRef<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - ) -> Self { - let baudrate = config.baudrate; - let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split(); - - let mut timer = Timer::new(timer); - - into_ref!(ppi_ch1, ppi_ch2); - - let r = U::regs(); - - // BAUDRATE register values are `baudrate * 2^32 / 16000000` - // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values - // - // We want to stop RX if line is idle for 2 bytes worth of time - // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) - // This gives us the amount of 16M ticks for 20 bits. - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - - timer.set_frequency(Frequency::F16MHz); - timer.cc(0).write(timeout); - timer.cc(0).short_compare_clear(); - timer.cc(0).short_compare_stop(); - - let mut ppi_ch1 = Ppi::new_one_to_two( - ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), - timer.task_clear(), - timer.task_start(), - ); - ppi_ch1.enable(); - - let mut ppi_ch2 = Ppi::new_one_to_one( - ppi_ch2.map_into(), - timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), - ); - ppi_ch2.enable(); - - Self { - tx, - rx: UarteRxWithIdle { - rx, - timer, - ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - }, - } - } - - /// Split the Uarte into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) { - (self.tx, self.rx) - } - - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.read(buffer).await - } - - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.write(buffer).await - } - - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.blocking_read(buffer) - } - - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.blocking_write(buffer) - } - - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.read_until_idle(buffer).await - } - - pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.blocking_read_until_idle(buffer) - } -} - -pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> { - rx: UarteRx<'d, U>, - timer: Timer<'d, T>, - ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, -} - -impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> { - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.ppi_ch1.disable(); - self.rx.read(buffer).await - } - - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.ppi_ch1.disable(); - self.rx.blocking_read(buffer) - } - - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let ptr = buffer.as_ptr(); - let len = buffer.len(); - - let r = U::regs(); - let s = U::state(); - - self.ppi_ch1.enable(); - - let drop = OnDrop::new(|| { - self.timer.stop(); - - r.intenclr.write(|w| w.endrx().clear()); - r.events_rxto.reset(); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - - while r.events_endrx.read().bits() == 0 {} - }); - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - - r.events_endrx.reset(); - r.intenset.write(|w| w.endrx().set()); - - compiler_fence(Ordering::SeqCst); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - poll_fn(|cx| { - s.endrx_waker.register(cx.waker()); - if r.events_endrx.read().bits() != 0 { - return Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; - - self.timer.stop(); - r.events_rxstarted.reset(); - - drop.defuse(); - - Ok(n) - } - - pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let ptr = buffer.as_ptr(); - let len = buffer.len(); - - let r = U::regs(); - - self.ppi_ch1.enable(); - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - - r.events_endrx.reset(); - r.intenclr.write(|w| w.endrx().clear()); - - compiler_fence(Ordering::SeqCst); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - while r.events_endrx.read().bits() == 0 {} - - compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; - - self.timer.stop(); - r.events_rxstarted.reset(); - - Ok(n) - } -} pub(crate) mod sealed { use core::sync::atomic::AtomicU8; @@ -1006,18 +908,6 @@ mod eh02 { Ok(()) } } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write for UarteWithIdle<'d, U, T> { - type Error = Error; - - fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn bflush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } } #[cfg(feature = "unstable-traits")] @@ -1067,10 +957,6 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { type Error = Error; } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> { - type Error = Error; - } } #[cfg(all( @@ -1126,26 +1012,4 @@ mod eha { self.read(buffer) } } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buffer) - } - } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } } diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs index 09ec624c..6af4f709 100644 --- a/examples/nrf/src/bin/uart_idle.rs +++ b/examples/nrf/src/bin/uart_idle.rs @@ -15,7 +15,8 @@ async fn main(_spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(UARTE0_UART0); - let mut uart = uarte::UarteWithIdle::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); info!("uarte initialized!"); @@ -23,12 +24,12 @@ async fn main(_spawner: Spawner) { let mut buf = [0; 8]; buf.copy_from_slice(b"Hello!\r\n"); - unwrap!(uart.write(&buf).await); + unwrap!(tx.write(&buf).await); info!("wrote hello in uart!"); loop { info!("reading..."); - let n = unwrap!(uart.read_until_idle(&mut buf).await); + let n = unwrap!(rx.read_until_idle(&mut buf).await); info!("got {} bytes", n); } } From 327d3cf0df7a1b116ea7ec44d36a569e6ba6ca16 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Mon, 10 Oct 2022 12:35:42 -0500 Subject: [PATCH 033/374] Change rak4631 feature to sx126x, removing use in board-specific processing; simplify the P2P examples; correct RSSI computation. --- embassy-lora/Cargo.toml | 4 +- embassy-lora/src/lib.rs | 2 +- .../src/sx126x/sx126x_lora/board_specific.rs | 28 ++----- .../src/sx126x/sx126x_lora/subroutine.rs | 6 +- examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/lora_p2p_report.rs | 62 +++++++------- examples/nrf/src/bin/lora_p2p_sense.rs | 84 ++++--------------- 7 files changed, 61 insertions(+), 127 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index bcb837d9..ea2c3fe6 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,7 +9,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] }, + { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] @@ -17,7 +17,7 @@ flavors = [ [lib] [features] -rak4631 = [] +sx126x = [] sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 342f66b2..3e474843 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -7,7 +7,7 @@ pub(crate) mod fmt; #[cfg(feature = "stm32wl")] pub mod stm32wl; -#[cfg(feature = "rak4631")] +#[cfg(feature = "sx126x")] pub mod sx126x; #[cfg(feature = "sx127x")] pub mod sx127x; diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs index 1fb08588..a7b9e148 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -10,8 +10,7 @@ use super::LoRa; // Defines the time required for the TCXO to wakeup [ms]. const BRD_TCXO_WAKEUP_TIME: u32 = 10; -// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. -// The base implementation supports the RAK4631 board. +// Provides board-specific functionality for Semtech SX126x-based boards. impl LoRa where @@ -203,44 +202,33 @@ where // Get the radio type pub(super) fn brd_get_radio_type(&mut self) -> RadioType { - #[cfg(feature = "rak4631")] RadioType::SX1262 } // Quiesce the antenna(s). pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_low().map_err(|_| AntRx)?; - } + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_low().map_err(|_| AntRx)?; Ok(()) } // Prepare the antenna(s) for a receive operation pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_high().map_err(|_| AntRx)?; - } + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_high().map_err(|_| AntRx)?; Ok(()) } // Prepare the antenna(s) for a send operation pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_rx.set_low().map_err(|_| AntRx)?; - self.antenna_tx.set_high().map_err(|_| AntTx)?; - } + self.antenna_rx.set_low().map_err(|_| AntRx)?; + self.antenna_tx.set_high().map_err(|_| AntTx)?; Ok(()) } // Check if the given RF frequency is supported by the hardware pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { - #[cfg(feature = "rak4631")] - Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 + Ok(true) } // Get the duration required for the TCXO to wakeup [ms]. diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs index 283e6099..2e78b919 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -564,7 +564,7 @@ where pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { let mut buffer = [0x00u8]; self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; - let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? + let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ??? Ok(rssi) } @@ -597,9 +597,9 @@ where self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; // check this ??? - let rssi = (-(status[0] as i8)) >> 1; + let rssi = ((-(status[0] as i32)) >> 1) as i8; let snr = ((status[1] as i8) + 2) >> 2; - let signal_rssi = (-(status[2] as i8)) >> 1; + let signal_rssi = ((-(status[2] as i32)) >> 1) as i8; let freq_error = self.frequency_error; Ok(PacketStatus { diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 91c41821..6949042e 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm 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 } embedded-io = "0.3.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "time", "defmt"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs index 4ba3d30c..d512b83f 100644 --- a/examples/nrf/src/bin/lora_p2p_report.rs +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -1,4 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. #![no_std] #![no_main] #![macro_use] @@ -18,7 +20,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M1; // M16 ??? + spi_config.frequency = spim::Frequency::M16; let mut radio = { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); @@ -47,38 +49,30 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_secs(5)).await; start_indicator.set_low(); - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), + loop { + let rf_config = RfConfig { + frequency: 903900000, // channel in Hz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }; + + let mut buffer = [00u8; 100]; + + // P2P receive + match radio.rx(rf_config, &mut buffer).await { + Ok((buffer_len, rx_quality)) => info!( + "RX received = {:?} with length = {} rssi = {} snr = {}", + &buffer[0..buffer_len], + buffer_len, + rx_quality.rssi(), + rx_quality.snr() + ), + Err(err) => info!("RX error = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(2)).await; + debug_indicator.set_low(); } - - let rf_config = RfConfig { - frequency: 903900000, // channel in Hz - bandwidth: Bandwidth::_250KHz, - spreading_factor: SpreadingFactor::_10, - coding_rate: CodingRate::_4_8, - }; - - let mut buffer = [00u8; 100]; - - // P2P receive - match radio.rx(rf_config, &mut buffer).await { - Ok((buffer_len, rx_quality)) => info!( - "RX received = {:?} with length = {} rssi = {} snr = {}", - &buffer[0..buffer_len], - buffer_len, - rx_quality.rssi(), - rx_quality.snr() - ), - Err(err) => info!("RX error = {}", err), - } - - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), - } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - debug_indicator.set_low(); } diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 405a8403..b9768874 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -1,4 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. #![no_std] #![no_main] #![macro_use] @@ -9,8 +11,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; -use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::temp::Temp; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; use embassy_nrf::{interrupt, spim}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher}; @@ -18,10 +19,6 @@ use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; use {defmt_rtt as _, panic_probe as _, panic_probe as _}; -// Sensor packet constants -const TEMPERATURE_UID: u8 = 0x01; -const MOTION_UID: u8 = 0x02; - // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); @@ -32,53 +29,20 @@ enum Message { } #[embassy_executor::task] -async fn temperature_task( - mut temperature: Temp<'static>, - publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, -) { - Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds - - let mut temperature_reporting_threshhold = 10; - +async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { + // Publish a fake temperature every 43 seconds, minimizing LORA traffic. loop { - let value = temperature.read().await; - let mut temperature_val = value.to_num::(); - - info!("Temperature: {}", temperature_val); - - // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report - - if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 { - temperature_val += 1; - } - - if temperature_reporting_threshhold > temperature_val - && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5) - { - temperature_reporting_threshhold = temperature_val; - publisher.publish(Message::Temperature(temperature_val)).await; - } else if temperature_val <= 3 { - publisher.publish(Message::Temperature(temperature_val)).await; - } - - Timer::after(Duration::from_secs(20 * 60)).await; + Timer::after(Duration::from_secs(43)).await; + publisher.publish(Message::Temperature(9)).await; } } #[embassy_executor::task] -async fn motion_detection_task( - mut pir_pin: Input<'static, AnyPin>, - publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, -) { - Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds - +async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { + // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. loop { - // wait for motion detection - pir_pin.wait_for_low().await; + Timer::after(Duration::from_secs(79)).await; publisher.publish(Message::MotionDetected).await; - - // wait a minute before setting up for more motion detection - Timer::after(Duration::from_secs(60)).await; } } @@ -91,7 +55,7 @@ async fn main(spawner: Spawner) { let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M1; // M16 ??? + spi_config.frequency = spim::Frequency::M16; let mut radio = { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); @@ -113,15 +77,7 @@ async fn main(spawner: Spawner) { } }; - // set up for the temperature task - let temperature_irq = interrupt::take!(TEMP); - let temperature = Temp::new(p.TEMP, temperature_irq); - - // set the motion detection pin - let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up); - let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); - let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); start_indicator.set_high(); Timer::after(Duration::from_secs(5)).await; @@ -132,15 +88,15 @@ async fn main(spawner: Spawner) { Err(err) => info!("Sleep unsuccessful = {}", err), } - unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher))); - unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher))); + unwrap!(spawner.spawn(temperature_task(temperature_publisher))); + unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); loop { let message = lora_tx_subscriber.next_message_pure().await; let tx_config = TxConfig { // 11 byte maximum payload for Bandwidth 125 and SF 10 - pw: 20, // up to 20 // 5 ??? + pw: 10, // up to 20 rf: RfConfig { frequency: 903900000, // channel in Hz, not MHz bandwidth: Bandwidth::_250KHz, @@ -149,13 +105,13 @@ async fn main(spawner: Spawner) { }, }; - let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8]; + let mut buffer = [0x00u8]; match message { - Message::Temperature(temperature) => buffer[1] = temperature as u8, - Message::MotionDetected => buffer[3] = 0x01u8, + Message::Temperature(temperature) => buffer[0] = temperature as u8, + Message::MotionDetected => buffer[0] = 0x01u8, }; - // crypto for text ??? + // unencrypted match radio.tx(tx_config, &buffer).await { Ok(ret_val) => info!("TX ret_val = {}", ret_val), Err(err) => info!("TX error = {}", err), @@ -165,9 +121,5 @@ async fn main(spawner: Spawner) { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - debug_indicator.set_low(); } } From 4da6320e63ad1c93944774c15a9fcc4641fe32ef Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 11 Oct 2022 10:20:31 +0200 Subject: [PATCH 034/374] Add note on partition sizes to bootloader docs See #1007 --- docs/modules/ROOT/pages/bootloader.adoc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index ae92e9d5..7dbfeb3e 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout] The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader instance or via linker scripts: -* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash. -* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. -* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. -* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped. +* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs. +* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application. +* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data: ++ +Partition Size~dfu~= Partition Size~active~+ Page Size~active~ ++ +All values are specified in bytes. + +* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: ++ +Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) ++ +All values are specified in bytes. The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. From 5846b4ff7d6a3ab177abe1f1070b0b21d5a87f1d Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 12 Oct 2022 10:54:47 +0200 Subject: [PATCH 035/374] Correctly enable RTC_IRQ when scheduling an RTC alarm --- embassy-rp/src/rtc/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 7f3bbbe7..e4b6f0b1 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -145,6 +145,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { filter.write_setup_1(w); }); + self.inner.regs().inte().modify(|w| w.set_rtc(true)); + // 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() { From a4afab46403aacd7bb555cadaefad7f216fb9931 Mon Sep 17 00:00:00 2001 From: pbert Date: Sun, 10 Jul 2022 20:12:25 +0200 Subject: [PATCH 036/374] add support for pdm microphones in nrf driver --- embassy-nrf/src/chips/nrf52810.rs | 3 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/chips/nrf9160.rs | 3 + embassy-nrf/src/lib.rs | 8 + embassy-nrf/src/pdm.rs | 242 ++++++++++++++++++++++++++++++ examples/nrf/src/bin/pdm.rs | 33 ++++ 8 files changed, 298 insertions(+) create mode 100644 embassy-nrf/src/pdm.rs create mode 100644 examples/nrf/src/bin/pdm.rs diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index faa52d8f..3e500098 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index bbdf1cbe..25c7c0d9 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 39a0f93f..3b33907d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -158,6 +158,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index e3d8f34a..ae59f8b2 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index a4be8564..f8ed11e0 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -260,6 +260,9 @@ embassy_hal_common::peripherals! { P0_29, P0_30, P0_31, + + // PDM + PDM, } impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d7bd2170..bc70fc2f 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -76,6 +76,14 @@ pub mod gpio; pub mod gpiote; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; +#[cfg(any( + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf9160" +))] +pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs new file mode 100644 index 00000000..b7c7022c --- /dev/null +++ b/embassy-nrf/src/pdm.rs @@ -0,0 +1,242 @@ +//! PDM mirophone interface + +use core::marker::PhantomData; +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 embassy_sync::waitqueue::AtomicWaker; +use futures::future::poll_fn; + +use crate::chip::EASY_DMA_SIZE; +use crate::gpio::sealed::Pin; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::{self, InterruptExt}; +use crate::peripherals::PDM; +use crate::{pac, Peripheral}; + +/// PDM microphone interface +pub struct Pdm<'d> { + irq: PeripheralRef<'d, interrupt::PDM>, + phantom: PhantomData<&'d PDM>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + BufferTooLong, + BufferZeroLength, + NotRunning, +} + +static WAKER: AtomicWaker = AtomicWaker::new(); +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +impl<'d> Pdm<'d> { + /// Create PDM driver + pub fn new( + pdm: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd, + din: impl Peripheral

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

+ 'd, + irq: impl Peripheral

+ 'd, + clk: PeripheralRef<'d, AnyPin>, + din: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(irq); + + let r = Self::regs(); + + // setup gpio pins + din.conf().write(|w| w.input().set_bit()); + r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + clk.set_low(); + clk.conf().write(|w| w.dir().output()); + r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + + // configure + // use default for + // - gain right + // - gain left + // - clk + // - ratio + r.mode.write(|w| { + w.edge().bit(config.edge == Edge::LeftRising); + w.operation().bit(config.operation_mode == OperationMode::Mono); + w + }); + r.gainl.write(|w| w.gainl().default_gain()); + r.gainr.write(|w| w.gainr().default_gain()); + + // IRQ + irq.disable(); + irq.set_handler(|_| { + let r = Self::regs(); + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + }); + irq.enable(); + + r.enable.write(|w| w.enable().set_bit()); + + Self { + phantom: PhantomData, + irq, + } + } + + /// Start sampling microphon data into a dummy buffer + /// Usefull to start the microphon and keep it active between recording samples + pub async fn start(&mut self) { + let r = Self::regs(); + + // start dummy sampling because microphon needs some setup time + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + r.tasks_start.write(|w| w.tasks_start().set_bit()); + } + + /// Stop sampling microphon data inta a dummy buffer + pub async fn stop(&mut self) { + let r = Self::regs(); + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.events_started.reset(); + } + + pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let r = Self::regs(); + + if r.events_started.read().events_started().bit_is_clear() { + return Err(Error::NotRunning); + } + + let drop = OnDrop::new(move || { + r.intenclr.write(|w| w.end().clear()); + r.events_stopped.reset(); + + // reset to dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + while r.events_stopped.read().bits() == 0 {} + }); + + // setup user buffer + let ptr = buffer.as_ptr(); + let len = buffer.len(); + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + + // wait till the current sample is finished and the user buffer sample is started + Self::wait_for_sample().await; + + // reset the buffer back to the dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + // wait till the user buffer is sampled + Self::wait_for_sample().await; + + drop.defuse(); + + Ok(()) + } + + async fn wait_for_sample() { + let r = Self::regs(); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + WAKER.register(cx.waker()); + if r.events_end.read().events_end().bit_is_set() { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + } + + fn regs() -> &'static pac::pdm::RegisterBlock { + unsafe { &*pac::PDM::ptr() } + } +} + +/// PDM microphone driver Config +pub struct Config { + /// Use stero or mono operation + pub operation_mode: OperationMode, + /// On which edge the left channel should be samples + pub edge: Edge, +} + +impl Default for Config { + fn default() -> Self { + Self { + operation_mode: OperationMode::Mono, + edge: Edge::LeftFalling, + } + } +} + +#[derive(PartialEq)] +pub enum OperationMode { + Mono, + Stereo, +} +#[derive(PartialEq)] +pub enum Edge { + LeftRising, + LeftFalling, +} + +impl<'d> Drop for Pdm<'d> { + fn drop(&mut self) { + let r = Self::regs(); + + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + + self.irq.disable(); + + r.enable.write(|w| w.enable().disabled()); + + r.psel.din.reset(); + r.psel.clk.reset(); + } +} diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs new file mode 100644 index 00000000..7388580f --- /dev/null +++ b/examples/nrf/src/bin/pdm.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::pdm::{Config, Pdm}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let p = embassy_nrf::init(Default::default()); + let config = Config::default(); + let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + + loop { + pdm.start().await; + + // wait some time till the microphon settled + Timer::after(Duration::from_millis(1000)).await; + + const SAMPLES: usize = 2048; + let mut buf = [0i16; SAMPLES]; + pdm.sample(&mut buf).await.unwrap(); + + info!("samples: {:?}", &buf); + + pdm.stop().await; + Timer::after(Duration::from_millis(100)).await; + } +} From e4c2b2aa9a61e5135d2e129705c9bdc3b4cb73ae Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 16 Oct 2022 18:00:23 -0700 Subject: [PATCH 037/374] rp i2c: have separate wakers for each i2c unit If they both share one waker, there's the possibility that some wakeups could get lost. --- embassy-rp/src/i2c.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 68cfc653..b9e4382c 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -70,7 +70,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -static I2C_WAKER: AtomicWaker = AtomicWaker::new(); +static I2C_WAKER: [AtomicWaker; 2] = [AtomicWaker::new(), AtomicWaker::new()]; impl<'d, T: Instance> I2c<'d, T, Async> { pub fn new_async( @@ -109,7 +109,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = f(self); if r.is_pending() { - I2C_WAKER.register(cx.waker()); + I2C_WAKER[T::IDX].register(cx.waker()); g(self); } r @@ -122,7 +122,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = T::regs(); i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - I2C_WAKER.wake(); + I2C_WAKER[T::IDX].wake(); } async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { @@ -813,6 +813,7 @@ mod sealed { pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; + const IDX: usize; type Interrupt: Interrupt; @@ -844,10 +845,11 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $idx:expr, $irq:ident, $reset: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; + const IDX: usize = $idx; type Interrupt = crate::interrupt::$irq; @@ -867,8 +869,8 @@ macro_rules! impl_instance { }; } -impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); +impl_instance!(I2C0, 0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, 1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From 02a3cdb507d535908bc0668a31526c05b5d01005 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 17 Oct 2022 21:50:40 -0700 Subject: [PATCH 038/374] Associate state with the instance rather than having a separate array --- embassy-rp/src/i2c.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index b9e4382c..d6742f6a 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -70,8 +70,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -static I2C_WAKER: [AtomicWaker; 2] = [AtomicWaker::new(), AtomicWaker::new()]; - impl<'d, T: Instance> I2c<'d, T, Async> { pub fn new_async( peri: impl Peripheral

+ 'd, @@ -109,7 +107,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = f(self); if r.is_pending() { - I2C_WAKER[T::IDX].register(cx.waker()); + T::waker().register(cx.waker()); g(self); } r @@ -122,7 +120,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = T::regs(); i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - I2C_WAKER[T::IDX].wake(); + T::waker().wake(); } async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { @@ -809,16 +807,17 @@ fn i2c_reserved_addr(addr: u16) -> bool { mod sealed { use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; - const IDX: usize; type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; fn reset() -> crate::pac::resets::regs::Peripherals; + fn waker() -> &'static AtomicWaker; } pub trait Mode {} @@ -845,11 +844,10 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $idx:expr, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $irq:ident, $reset: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; - const IDX: usize = $idx; type Interrupt = crate::interrupt::$irq; @@ -864,13 +862,20 @@ macro_rules! impl_instance { ret.$reset(true); ret } + + #[inline] + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + + &WAKER + } } impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, 0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, 1, I2C1_IRQ, set_i2c1, 34, 35); +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From 6c5d81ada52bfac1592c7be025038a3eea3acc42 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 18 Oct 2022 22:42:02 +0200 Subject: [PATCH 039/374] Add memory barriers to H7 flash driver to mitigate PGSERR errors The stm32h7xx-hal uses only the ordering barrier, while the CubeMX uses the DSB and ISB instructions, to be on the safe side, both are used here. --- embassy-stm32/src/flash/h7.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 7ce0ac77..3f2129de 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -39,6 +39,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error w.set_psize(2); // 32 bits at once }); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + let ret = { let mut ret: Result<(), Error> = Ok(()); let mut offset = offset; @@ -64,6 +68,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error bank.cr().write(|w| w.set_pg(false)); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + ret } From 9d2641f2f5f96e85f3c900a69388071f1adddfdc Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Mon, 17 Oct 2022 15:50:46 +0200 Subject: [PATCH 040/374] Enable defmt in embassy-hal-common --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 2610e568..9194ae78 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -75,7 +75,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-driver?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] From fa495b8e889b6fec2b44f5c59c69ea4fe798f4ec Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 19 Oct 2022 03:36:18 -0500 Subject: [PATCH 041/374] Add missing examples to rust-analyzer linked projects --- .vscode/settings.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5e9e5179..62e5a362 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,10 +20,13 @@ //"embassy-executor/Cargo.toml", //"embassy-sync/Cargo.toml", "examples/nrf/Cargo.toml", + // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", // "examples/stm32f1/Cargo.toml", + // "examples/stm32f2/Cargo.toml", + // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", @@ -32,8 +35,11 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", + // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", + // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", + // "examples/stm32wl/Cargo.toml", // "examples/stm32wl55/Cargo.toml", // "examples/wasm/Cargo.toml", ], From de103a5f4fba5e913988928e22301b4ee05e8515 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 20 Oct 2022 16:12:59 +0200 Subject: [PATCH 042/374] Add missing files and features for basic example --- docs/modules/ROOT/examples/basic/Cargo.toml | 5 ++- docs/modules/ROOT/examples/basic/build.rs | 35 +++++++++++++++++++++ docs/modules/ROOT/examples/basic/memory.x | 7 +++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 docs/modules/ROOT/examples/basic/build.rs create mode 100644 docs/modules/ROOT/examples/basic/memory.x diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index c13f546e..d9f8a285 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,14 +6,13 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } +embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } defmt = "0.3" defmt-rtt = "0.3" -cortex-m = "0.7.3" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/docs/modules/ROOT/examples/basic/build.rs b/docs/modules/ROOT/examples/basic/build.rs new file mode 100644 index 00000000..30691aa9 --- /dev/null +++ b/docs/modules/ROOT/examples/basic/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/docs/modules/ROOT/examples/basic/memory.x b/docs/modules/ROOT/examples/basic/memory.x new file mode 100644 index 00000000..9b04edec --- /dev/null +++ b/docs/modules/ROOT/examples/basic/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} From f45d34ce7c5663da9e3c96ab698e8a84dbafb4ac Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 20 Oct 2022 23:46:02 +0800 Subject: [PATCH 043/374] Fix mistaken EP_IN_WAKERS --- embassy-rp/src/usb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 0a904aab..8d6aa33b 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -522,7 +522,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { trace!("wait_enabled IN WAITING"); let index = self.info.addr.index(); poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); + EP_IN_WAKERS[index].register(cx.waker()); let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; if val.enable() { Poll::Ready(()) From 866a42f3aeb196e2b0f854a52683ee07cffc4ccd Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 21 Oct 2022 22:02:13 +0800 Subject: [PATCH 044/374] rp usb: wait for accept() completion This ensures that the current response has finished being sent before the subsequent set_address() happens. Otherwise connecting a device is intermittent, can fail depending on timing. --- embassy-rp/src/usb.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 0a904aab..7a83dcb4 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -811,8 +811,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { async move { trace!("control: accept"); + let bufcontrol = T::dpram().ep_in_buffer_control(0); unsafe { - let bufcontrol = T::dpram().ep_in_buffer_control(0); bufcontrol.write(|w| { w.set_length(0, 0); w.set_pid(0, true); @@ -826,6 +826,18 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { w.set_available(0, true); }); } + + // wait for completion before returning, needed so + // set_address() doesn't happen early. + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + if unsafe { bufcontrol.read().available(0) } { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; } } From c2404ee8ca6200d9037f096eee3f0eab98711778 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 6 Sep 2022 21:39:23 +0300 Subject: [PATCH 045/374] Initial generic timer queue impl --- embassy-time/Cargo.toml | 5 + embassy-time/src/lib.rs | 1 + embassy-time/src/queue.rs | 197 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 embassy-time/src/queue.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c51a71d0..0e3391d1 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,6 +26,9 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] +# TODO: Doc +generic-queue = [] + # Set the `embassy_time` tick rate. # # At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. @@ -111,9 +114,11 @@ embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} +embassy-sync = { version = "0.1", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" cfg-if = "1.0.0" +heapless = "0.7" # WASM dependencies wasm-bindgen = { version = "0.2.81", optional = true } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 4edc883f..0457a657 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -11,6 +11,7 @@ mod delay; pub mod driver; mod duration; mod instant; +pub mod queue; mod tick; mod timer; diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs new file mode 100644 index 00000000..7e84090b --- /dev/null +++ b/embassy-time/src/queue.rs @@ -0,0 +1,197 @@ +//! Generic timer queue implementation +use core::cell::RefCell; +use core::cmp::Ordering; +use core::task::Waker; + +use atomic_polyfill::{AtomicBool, Ordering as AtomicOrdering}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use embassy_sync::blocking_mutex::Mutex; +use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; + +use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use crate::Instant; + +#[derive(Debug)] +struct Timer { + at: Instant, + waker: Waker, +} + +impl PartialEq for Timer { + fn eq(&self, other: &Self) -> bool { + self.at == other.at + } +} + +impl Eq for Timer {} + +impl PartialOrd for Timer { + fn partial_cmp(&self, other: &Self) -> Option { + self.at.partial_cmp(&other.at) + } +} + +impl Ord for Timer { + fn cmp(&self, other: &Self) -> Ordering { + self.at.cmp(&other.at) + } +} + +struct InnerQueue { + queue: SortedLinkedList, + alarm_at: Instant, + alarm: Option, +} + +impl InnerQueue { + const fn new() -> Self { + Self { + queue: SortedLinkedList::new_u8(), + alarm_at: Instant::MAX, + alarm: None, + } + } + + fn schedule(&mut self, at: Instant, waker: &Waker) { + self.queue + .find_mut(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.waker = waker.clone(); + timer.at = at; + + timer.finish(); + }) + .unwrap_or_else(|| { + let mut timer = Timer { + waker: waker.clone(), + at, + }; + + loop { + match self.queue.push(timer) { + Ok(()) => break, + Err(e) => timer = e, + } + + self.queue.pop().unwrap().waker.wake(); + } + }); + + // Don't wait for the alarm callback to trigger and directly + // dispatch all timers that are already due + // + // Then update the alarm if necessary + self.dispatch(); + } + + fn dispatch(&mut self) { + let now = Instant::now(); + + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + self.update_alarm(); + } + + fn update_alarm(&mut self) { + if let Some(timer) = self.queue.peek() { + let new_at = timer.at; + + if self.alarm_at != new_at { + self.alarm_at = new_at; + set_alarm(self.alarm.unwrap(), new_at.as_ticks()); + } + } else { + self.alarm_at = Instant::MAX; + } + } + + fn handle_alarm(&mut self) { + self.alarm_at = Instant::MAX; + + self.dispatch(); + } +} + +/// TODO: Doc +pub struct Queue { + initialized: AtomicBool, + inner: Mutex>>, +} + +impl Queue { + /// TODO: Doc + pub const fn new() -> Self { + Self { + initialized: AtomicBool::new(false), + inner: Mutex::new(RefCell::new(InnerQueue::::new())), + } + } + + /// TODO: Doc + pub unsafe fn initialize(&'static self) { + if self.initialized.load(AtomicOrdering::SeqCst) { + panic!("Queue already initialized"); + } + + let handle = allocate_alarm().unwrap(); + self.inner.lock(|inner| inner.borrow_mut().alarm = Some(handle)); + + set_alarm_callback(handle, Self::handle_alarm, self as *const _ as _); + + self.initialized.store(true, AtomicOrdering::SeqCst); + } + + /// TODO: Doc + pub fn schedule(&'static self, at: Instant, waker: &Waker) { + self.check_initialized(); + + self.inner.lock(|inner| inner.borrow_mut().schedule(at, waker)); + } + + fn check_initialized(&self) { + if !self.initialized.load(AtomicOrdering::SeqCst) { + panic!("Queue is not initialized"); + } + } + + fn handle_alarm(ctx: *mut ()) { + let this = unsafe { (ctx as *const Self).as_ref().unwrap() }; + + this.check_initialized(); + this.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + } +} + +/// TODO: Doc +pub unsafe fn initialize() { + extern "Rust" { + fn _embassy_time_generic_queue_initialize(); + } + + _embassy_time_generic_queue_initialize(); +} + +/// TODO: Doc +#[macro_export] +macro_rules! generic_queue { + (static $name:ident: $t: ty = $val:expr) => { + static $name: $t = $val; + + #[no_mangle] + fn _embassy_time_generic_queue_initialize() { + unsafe { + $crate::queue::Queue::initialize(&$name); + } + } + + #[no_mangle] + fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { + $crate::queue::Queue::schedule(&$name, at, waker); + } + }; +} + +#[cfg(feature = "generic-queue")] +generic_queue!(static QUEUE: Queue = Queue::new()); From ba6e452cc5d6c33029f34d7cfb5cd5ea846979bd Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 20 Sep 2022 20:23:56 +0300 Subject: [PATCH 046/374] Documentation and initial testing framework Add mock waker First simple test Tests & documentation --- embassy-time/Cargo.toml | 6 +- embassy-time/src/lib.rs | 2 +- embassy-time/src/queue.rs | 486 +++++++++++++++++++++++++++++++++++--- 3 files changed, 459 insertions(+), 35 deletions(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0e3391d1..4fbf97f0 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,7 +26,8 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] -# TODO: Doc +# Create a global queue that can be used with any executor +# To use this you must have a time driver provided. generic-queue = [] # Set the `embassy_time` tick rate. @@ -124,3 +125,6 @@ heapless = "0.7" wasm-bindgen = { version = "0.2.81", optional = true } js-sys = { version = "0.3", optional = true } wasm-timer = { version = "0.2.5", optional = true } + +[dev-dependencies] +serial_test = "0.9" diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 0457a657..50f437ba 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] +#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] #![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs index 7e84090b..56ad5af8 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time/src/queue.rs @@ -1,9 +1,53 @@ //! Generic timer queue implementation -use core::cell::RefCell; +//! +//! This module provides a timer queue that works with any executor. +//! +//! In terms of performance, this queue will likely be less efficient in comparison to executor-native queues, +//! like the one provided with e.g. the `embassy-executor` crate. +//! +//! # Enabling the queue +//! - Enable the Cargo feature `generic-queue`. This will automatically instantiate the queue. +//! +//! # Initializing the queue +//! - Call ```unsafe { embassy_time::queue::initialize(); }``` early on in your program, before any of your futures that utilize `embassy-time` are polled. +//! +//! # Customizing the queue +//! - It is possible to customize two aspects of the queue: +//! - Queue size: +//! By default, the queue can hold up to 128 timer schedules and their corresponding wakers. While it will not crash if more timer schedules are added, +//! the performance will degrade, as one of the already added wakers will be awoken, thus making room for the new timer schedule and its waker. +//! - The mutex (i.e. the [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation) utilized by the queue: +//! By default, the utilized [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation is [`CriticalSectionRawMutex`](embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex) +//! which is provided by the `critical-section` crate. This should work just fine, except in a few niche cases like running on +//! top of an RTOS which provides a [`Driver`](crate::driver::Driver) implementation that will call-back directly from an ISR. As the +//! `critical-section` implementation for RTOS-es will likely provide an RTOS mutex which cannot be locked from an ISR, user needs to instead +//! configure the queue with a "disable-all-interrupts" style of mutex. +//! - To customize any of these queue aspects, don't enable the `generic-queue` Cargo feature and instead instantiate the queue with the [`generic_queue`](crate::generic_queue) +//! macro, as per the example below. +//! +//! +//! # Example +//! +//! ```ignore +//! use embassy_time::queue::Queue; +//! +//! // You only need to invoke this macro in case you need to customize the queue. +//! // +//! // Otherwise, just depend on the `embassy-time` crate with feature `generic-queue` enabled, +//! // and the queue instantiation will be done for you behind the scenes. +//! embassy_time::generic_queue!(static QUEUE: Queue<200, MyCustomRawMutex> = Queue::new()); +//! +//! fn main() { +//! unsafe { +//! embassy_time::queue::initialize(); +//! } +//! } +//! ``` +use core::cell::{Cell, RefCell}; use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicBool, Ordering as AtomicOrdering}; +use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use embassy_sync::blocking_mutex::Mutex; use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; @@ -40,7 +84,6 @@ impl Ord for Timer { struct InnerQueue { queue: SortedLinkedList, alarm_at: Instant, - alarm: Option, } impl InnerQueue { @@ -48,11 +91,10 @@ impl InnerQueue { Self { queue: SortedLinkedList::new_u8(), alarm_at: Instant::MAX, - alarm: None, } } - fn schedule(&mut self, at: Instant, waker: &Waker) { + fn schedule(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { @@ -81,90 +123,128 @@ impl InnerQueue { // dispatch all timers that are already due // // Then update the alarm if necessary - self.dispatch(); + self.dispatch(alarm_schedule); } - fn dispatch(&mut self) { + fn dispatch(&mut self, alarm_schedule: &AtomicU64) { let now = Instant::now(); while self.queue.peek().filter(|timer| timer.at <= now).is_some() { self.queue.pop().unwrap().waker.wake(); } - self.update_alarm(); + self.update_alarm(alarm_schedule); } - fn update_alarm(&mut self) { + fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { if let Some(timer) = self.queue.peek() { let new_at = timer.at; if self.alarm_at != new_at { self.alarm_at = new_at; - set_alarm(self.alarm.unwrap(), new_at.as_ticks()); + alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); } } else { self.alarm_at = Instant::MAX; + alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); } } - fn handle_alarm(&mut self) { + fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { self.alarm_at = Instant::MAX; - self.dispatch(); + self.dispatch(alarm_schedule); } } -/// TODO: Doc +/// The generic queue implementation pub struct Queue { - initialized: AtomicBool, inner: Mutex>>, + alarm: Cell>, + alarm_schedule: AtomicU64, } impl Queue { - /// TODO: Doc + /// Create a Queue pub const fn new() -> Self { Self { - initialized: AtomicBool::new(false), inner: Mutex::new(RefCell::new(InnerQueue::::new())), + alarm: Cell::new(None), + alarm_schedule: AtomicU64::new(u64::MAX), } } - /// TODO: Doc + /// Initialize the queue + /// + /// This method is called from [`initialize`](crate::queue::initialize), so you are not expected to call it directly. + /// Call [`initialize`](crate::queue::initialize) instead. + /// + /// # Safety + /// It is UB call this function more than once, or to call it after any of your + /// futures that use `embassy-time` are polled already. pub unsafe fn initialize(&'static self) { - if self.initialized.load(AtomicOrdering::SeqCst) { - panic!("Queue already initialized"); + if self.alarm.get().is_some() { + panic!("Queue is already initialized"); } let handle = allocate_alarm().unwrap(); - self.inner.lock(|inner| inner.borrow_mut().alarm = Some(handle)); + self.alarm.set(Some(handle)); - set_alarm_callback(handle, Self::handle_alarm, self as *const _ as _); - - self.initialized.store(true, AtomicOrdering::SeqCst); + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); } - /// TODO: Doc + /// Schedule a new waker to be awoken at moment `at` + /// + /// This method is called internally by [`embassy-time`](crate), so you are not expected to call it directly. pub fn schedule(&'static self, at: Instant, waker: &Waker) { self.check_initialized(); - self.inner.lock(|inner| inner.borrow_mut().schedule(at, waker)); + self.inner + .lock(|inner| inner.borrow_mut().schedule(at, waker, &self.alarm_schedule)); + + self.update_alarm(); } fn check_initialized(&self) { - if !self.initialized.load(AtomicOrdering::SeqCst) { - panic!("Queue is not initialized"); + if self.alarm.get().is_none() { + panic!("Queue is not initialized yet"); } } - fn handle_alarm(ctx: *mut ()) { - let this = unsafe { (ctx as *const Self).as_ref().unwrap() }; + fn update_alarm(&self) { + // Need to set the alarm when we are *not* holding the mutex on the inner queue + // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately + // call us back if the timestamp is in the past. + let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - this.check_initialized(); - this.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + if alarm_at < u64::MAX { + set_alarm(self.alarm.get().unwrap(), alarm_at); + } + } + + fn handle_alarm(&self) { + self.check_initialized(); + self.inner + .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); + + self.update_alarm(); + } + + fn handle_alarm_callback(ctx: *mut ()) { + unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); } } -/// TODO: Doc +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + +/// Initialize the queue +/// +/// Call this function early on in your program, before any of your futures that utilize `embassy-time` are polled. +/// +/// # Safety +/// It is UB call this function more than once, or to call it after any of your +/// futures that use `embassy-time` are polled already. pub unsafe fn initialize() { extern "Rust" { fn _embassy_time_generic_queue_initialize(); @@ -173,7 +253,12 @@ pub unsafe fn initialize() { _embassy_time_generic_queue_initialize(); } -/// TODO: Doc +/// Instantiates a global, generic (as in executor-agnostic) timer queue. +/// +/// Unless you plan to customize the queue (size or mutex), prefer +/// instantiating the queue via the `generic-queue` feature. +/// +/// See the module documentation for an example. #[macro_export] macro_rules! generic_queue { (static $name:ident: $t: ty = $val:expr) => { @@ -195,3 +280,338 @@ macro_rules! generic_queue { #[cfg(feature = "generic-queue")] generic_queue!(static QUEUE: Queue = Queue::new()); + +#[cfg(test)] +mod tests { + use core::cell::Cell; + use core::sync::atomic::Ordering; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use std::rc::Rc; + use std::sync::Mutex; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use serial_test::serial; + + use super::InnerQueue; + use crate::driver::{AlarmHandle, Driver}; + use crate::Instant; + + struct InnerTestDriver { + now: u64, + alarm: u64, + callback: fn(*mut ()), + ctx: *mut (), + } + + impl InnerTestDriver { + const fn new() -> Self { + Self { + now: 0, + alarm: u64::MAX, + callback: Self::noop, + ctx: core::ptr::null_mut(), + } + } + + fn noop(_ctx: *mut ()) {} + } + + unsafe impl Send for InnerTestDriver {} + + struct TestDriver(Mutex); + + impl TestDriver { + const fn new() -> Self { + Self(Mutex::new(InnerTestDriver::new())) + } + + fn reset(&self) { + *self.0.lock().unwrap() = InnerTestDriver::new(); + } + + fn set_now(&self, now: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if inner.now < now { + inner.now = now; + + if inner.alarm <= now { + inner.alarm = u64::MAX; + + Some((inner.callback, inner.ctx)) + } else { + None + } + } else { + panic!("Going back in time?"); + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + impl Driver for TestDriver { + fn now(&self) -> u64 { + self.0.lock().unwrap().now + } + + unsafe fn allocate_alarm(&self) -> Option { + Some(AlarmHandle::new(0)) + } + + fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + let mut inner = self.0.lock().unwrap(); + + inner.callback = callback; + inner.ctx = ctx; + } + + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + Some((inner.callback, inner.ctx)) + } else { + inner.alarm = timestamp; + None + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + struct TestWaker { + pub awoken: Rc>, + pub waker: Waker, + } + + impl TestWaker { + fn new() -> Self { + let flag = Rc::new(Cell::new(false)); + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + |data: *const ()| { + unsafe { + Rc::increment_strong_count(data as *const Cell); + } + + RawWaker::new(data as _, &VTABLE) + }, + |data: *const ()| unsafe { + let data = data as *const Cell; + data.as_ref().unwrap().set(true); + Rc::decrement_strong_count(data); + }, + |data: *const ()| unsafe { + (data as *const Cell).as_ref().unwrap().set(true); + }, + |data: *const ()| unsafe { + Rc::decrement_strong_count(data); + }, + ); + + let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); + + Self { + awoken: flag.clone(), + waker: unsafe { Waker::from_raw(raw) }, + } + } + } + + // TODO: This impl should be part of `embassy-sync`, hidden behind the "std" feature gate + pub struct StdRawMutex(std::sync::Mutex<()>); + + unsafe impl RawMutex for StdRawMutex { + const INIT: Self = StdRawMutex(std::sync::Mutex::new(())); + + fn lock(&self, f: impl FnOnce() -> R) -> R { + let _guard = self.0.lock().unwrap(); + + f() + } + } + + const QUEUE_MAX_LEN: usize = 8; + + crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); + crate::generic_queue!(static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new()); + + fn setup() { + DRIVER.reset(); + + QUEUE.alarm.set(None); + QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); + QUEUE.inner.lock(|inner| { + *inner.borrow_mut() = InnerQueue::new(); + }); + + unsafe { super::initialize() }; + } + + fn queue_len() -> usize { + QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + } + + #[test] + #[serial] + #[should_panic(expected = "Queue is not initialized yet")] + fn test_not_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + } + + #[test] + #[serial] + fn test_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + assert!(QUEUE.alarm.get().is_none()); + + unsafe { QUEUE.initialize() }; + + assert!(QUEUE.alarm.get().is_some()); + } + + #[test] + #[serial] + #[should_panic(expected = "Queue is already initialized")] + fn test_already_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + unsafe { QUEUE.initialize() }; + + assert!(QUEUE.alarm.get().is_some()); + + unsafe { QUEUE.initialize() }; + } + + #[test] + #[serial] + fn test_schedule() { + setup(); + + assert_eq!(queue_len(), 0); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert!(!waker.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_schedule_same() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + assert_eq!(queue_len(), 1); + + let waker2 = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker2.waker); + + assert_eq!(queue_len(), 2); + } + + #[test] + #[serial] + fn test_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + assert!(!waker.awoken.get()); + + DRIVER.set_now(Instant::from_secs(99).as_ticks()); + + assert!(!waker.awoken.get()); + + assert_eq!(queue_len(), 1); + + DRIVER.set_now(Instant::from_secs(100).as_ticks()); + + assert!(waker.awoken.get()); + + assert_eq!(queue_len(), 0); + } + + #[test] + #[serial] + fn test_immediate_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + DRIVER.set_now(Instant::from_secs(50).as_ticks()); + + let waker2 = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(40), &waker2.waker); + + assert!(!waker.awoken.get()); + assert!(waker2.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_queue_overflow() { + setup(); + + for i in 1..QUEUE_MAX_LEN { + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(310), &waker.waker); + + assert_eq!(queue_len(), i); + assert!(!waker.awoken.get()); + } + + let first_waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(300), &first_waker.waker); + + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(!first_waker.awoken.get()); + + let second_waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(305), &second_waker.waker); + + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(first_waker.awoken.get()); + + QUEUE.schedule(Instant::from_secs(320), &TestWaker::new().waker); + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(second_waker.awoken.get()); + } +} From 53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 26 Sep 2022 13:46:15 +0300 Subject: [PATCH 047/374] Address feedback after code review --- embassy-time/Cargo.toml | 16 +- embassy-time/src/lib.rs | 2 + embassy-time/src/queue.rs | 613 ++---------------------------- embassy-time/src/queue_generic.rs | 474 +++++++++++++++++++++++ 4 files changed, 518 insertions(+), 587 deletions(-) create mode 100644 embassy-time/src/queue_generic.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 4fbf97f0..e1ad4b9d 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,10 +26,22 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] -# Create a global queue that can be used with any executor +# Create a global, generic queue that can be used with any executor # To use this you must have a time driver provided. generic-queue = [] +# Set the number of timers for the generic queue. +# +# At most 1 `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. +# +# When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the +# end user to pick. +generic-queue-8 = ["generic-queue"] +generic-queue-16 = ["generic-queue"] +generic-queue-32 = ["generic-queue"] +generic-queue-64 = ["generic-queue"] +generic-queue-128 = ["generic-queue"] + # Set the `embassy_time` tick rate. # # At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. @@ -128,3 +140,5 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" +critical-section = { version = "1.1", features = ["std"] } + diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 50f437ba..586aa28d 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -19,6 +19,8 @@ mod timer; mod driver_std; #[cfg(feature = "wasm")] mod driver_wasm; +#[cfg(feature = "generic-queue")] +mod queue_generic; pub use delay::{block_for, Delay}; pub use duration::Duration; diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs index 56ad5af8..c6f8b440 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time/src/queue.rs @@ -1,617 +1,58 @@ -//! Generic timer queue implementation +//! Timer queue implementation //! -//! This module provides a timer queue that works with any executor. +//! This module defines the interface a timer queue needs to implement to power the `embassy_time` module. //! -//! In terms of performance, this queue will likely be less efficient in comparison to executor-native queues, -//! like the one provided with e.g. the `embassy-executor` crate. +//! # Implementing a timer queue //! -//! # Enabling the queue -//! - Enable the Cargo feature `generic-queue`. This will automatically instantiate the queue. +//! - Define a struct `MyTimerQueue` +//! - Implement [`TimerQueue`] for it +//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). //! -//! # Initializing the queue -//! - Call ```unsafe { embassy_time::queue::initialize(); }``` early on in your program, before any of your futures that utilize `embassy-time` are polled. +//! # Linkage details //! -//! # Customizing the queue -//! - It is possible to customize two aspects of the queue: -//! - Queue size: -//! By default, the queue can hold up to 128 timer schedules and their corresponding wakers. While it will not crash if more timer schedules are added, -//! the performance will degrade, as one of the already added wakers will be awoken, thus making room for the new timer schedule and its waker. -//! - The mutex (i.e. the [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation) utilized by the queue: -//! By default, the utilized [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation is [`CriticalSectionRawMutex`](embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex) -//! which is provided by the `critical-section` crate. This should work just fine, except in a few niche cases like running on -//! top of an RTOS which provides a [`Driver`](crate::driver::Driver) implementation that will call-back directly from an ISR. As the -//! `critical-section` implementation for RTOS-es will likely provide an RTOS mutex which cannot be locked from an ISR, user needs to instead -//! configure the queue with a "disable-all-interrupts" style of mutex. -//! - To customize any of these queue aspects, don't enable the `generic-queue` Cargo feature and instead instantiate the queue with the [`generic_queue`](crate::generic_queue) -//! macro, as per the example below. +//! Check the documentation of the [`driver`](crate::driver) module for more information. //! +//! Similarly to driver, if there is none or multiple timer queues in the crate tree, linking will fail. //! //! # Example //! -//! ```ignore -//! use embassy_time::queue::Queue; +//! ``` +//! use core::task::Waker; //! -//! // You only need to invoke this macro in case you need to customize the queue. -//! // -//! // Otherwise, just depend on the `embassy-time` crate with feature `generic-queue` enabled, -//! // and the queue instantiation will be done for you behind the scenes. -//! embassy_time::generic_queue!(static QUEUE: Queue<200, MyCustomRawMutex> = Queue::new()); +//! use embassy_time::Instant; +//! use embassy_time::queue::{TimerQueue}; //! -//! fn main() { -//! unsafe { -//! embassy_time::queue::initialize(); +//! struct MyTimerQueue{}; // not public! +//! embassy_time::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); +//! +//! impl TimerQueue for MyTimerQueue { +//! fn schedule_wake(&'static self, at: Instant, waker: &Waker) { +//! todo!() //! } //! } //! ``` -use core::cell::{Cell, RefCell}; -use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; -use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; -use embassy_sync::blocking_mutex::Mutex; -use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; - -use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; use crate::Instant; -#[derive(Debug)] -struct Timer { - at: Instant, - waker: Waker, +/// Timer queue +pub trait TimerQueue { + /// Schedules a waker in the queue to be awoken at moment `at`. + /// If this moment is in the past, the waker might be awoken immediately. + fn schedule_wake(&'static self, at: Instant, waker: &Waker); } -impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.at == other.at - } -} - -impl Eq for Timer {} - -impl PartialOrd for Timer { - fn partial_cmp(&self, other: &Self) -> Option { - self.at.partial_cmp(&other.at) - } -} - -impl Ord for Timer { - fn cmp(&self, other: &Self) -> Ordering { - self.at.cmp(&other.at) - } -} - -struct InnerQueue { - queue: SortedLinkedList, - alarm_at: Instant, -} - -impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm_at: Instant::MAX, - } - } - - fn schedule(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { - self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) - .map(|mut timer| { - timer.waker = waker.clone(); - timer.at = at; - - timer.finish(); - }) - .unwrap_or_else(|| { - let mut timer = Timer { - waker: waker.clone(), - at, - }; - - loop { - match self.queue.push(timer) { - Ok(()) => break, - Err(e) => timer = e, - } - - self.queue.pop().unwrap().waker.wake(); - } - }); - - // Don't wait for the alarm callback to trigger and directly - // dispatch all timers that are already due - // - // Then update the alarm if necessary - self.dispatch(alarm_schedule); - } - - fn dispatch(&mut self, alarm_schedule: &AtomicU64) { - let now = Instant::now(); - - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); - } - - self.update_alarm(alarm_schedule); - } - - fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); - } - } else { - self.alarm_at = Instant::MAX; - alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); - } - } - - fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { - self.alarm_at = Instant::MAX; - - self.dispatch(alarm_schedule); - } -} - -/// The generic queue implementation -pub struct Queue { - inner: Mutex>>, - alarm: Cell>, - alarm_schedule: AtomicU64, -} - -impl Queue { - /// Create a Queue - pub const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(InnerQueue::::new())), - alarm: Cell::new(None), - alarm_schedule: AtomicU64::new(u64::MAX), - } - } - - /// Initialize the queue - /// - /// This method is called from [`initialize`](crate::queue::initialize), so you are not expected to call it directly. - /// Call [`initialize`](crate::queue::initialize) instead. - /// - /// # Safety - /// It is UB call this function more than once, or to call it after any of your - /// futures that use `embassy-time` are polled already. - pub unsafe fn initialize(&'static self) { - if self.alarm.get().is_some() { - panic!("Queue is already initialized"); - } - - let handle = allocate_alarm().unwrap(); - self.alarm.set(Some(handle)); - - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - } - - /// Schedule a new waker to be awoken at moment `at` - /// - /// This method is called internally by [`embassy-time`](crate), so you are not expected to call it directly. - pub fn schedule(&'static self, at: Instant, waker: &Waker) { - self.check_initialized(); - - self.inner - .lock(|inner| inner.borrow_mut().schedule(at, waker, &self.alarm_schedule)); - - self.update_alarm(); - } - - fn check_initialized(&self) { - if self.alarm.get().is_none() { - panic!("Queue is not initialized yet"); - } - } - - fn update_alarm(&self) { - // Need to set the alarm when we are *not* holding the mutex on the inner queue - // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately - // call us back if the timestamp is in the past. - let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - - if alarm_at < u64::MAX { - set_alarm(self.alarm.get().unwrap(), alarm_at); - } - } - - fn handle_alarm(&self) { - self.check_initialized(); - self.inner - .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); - - self.update_alarm(); - } - - fn handle_alarm_callback(ctx: *mut ()) { - unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); - } -} - -unsafe impl Send for Queue {} -unsafe impl Sync for Queue {} - -/// Initialize the queue -/// -/// Call this function early on in your program, before any of your futures that utilize `embassy-time` are polled. -/// -/// # Safety -/// It is UB call this function more than once, or to call it after any of your -/// futures that use `embassy-time` are polled already. -pub unsafe fn initialize() { - extern "Rust" { - fn _embassy_time_generic_queue_initialize(); - } - - _embassy_time_generic_queue_initialize(); -} - -/// Instantiates a global, generic (as in executor-agnostic) timer queue. -/// -/// Unless you plan to customize the queue (size or mutex), prefer -/// instantiating the queue via the `generic-queue` feature. +/// Set the TimerQueue implementation. /// /// See the module documentation for an example. #[macro_export] -macro_rules! generic_queue { +macro_rules! timer_queue_impl { (static $name:ident: $t: ty = $val:expr) => { static $name: $t = $val; - #[no_mangle] - fn _embassy_time_generic_queue_initialize() { - unsafe { - $crate::queue::Queue::initialize(&$name); - } - } - #[no_mangle] fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { - $crate::queue::Queue::schedule(&$name, at, waker); + <$t as $crate::queue::TimerQueue>::schedule_wake(&$name, at, waker); } }; } - -#[cfg(feature = "generic-queue")] -generic_queue!(static QUEUE: Queue = Queue::new()); - -#[cfg(test)] -mod tests { - use core::cell::Cell; - use core::sync::atomic::Ordering; - use core::task::{RawWaker, RawWakerVTable, Waker}; - use std::rc::Rc; - use std::sync::Mutex; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use serial_test::serial; - - use super::InnerQueue; - use crate::driver::{AlarmHandle, Driver}; - use crate::Instant; - - struct InnerTestDriver { - now: u64, - alarm: u64, - callback: fn(*mut ()), - ctx: *mut (), - } - - impl InnerTestDriver { - const fn new() -> Self { - Self { - now: 0, - alarm: u64::MAX, - callback: Self::noop, - ctx: core::ptr::null_mut(), - } - } - - fn noop(_ctx: *mut ()) {} - } - - unsafe impl Send for InnerTestDriver {} - - struct TestDriver(Mutex); - - impl TestDriver { - const fn new() -> Self { - Self(Mutex::new(InnerTestDriver::new())) - } - - fn reset(&self) { - *self.0.lock().unwrap() = InnerTestDriver::new(); - } - - fn set_now(&self, now: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); - - if inner.now < now { - inner.now = now; - - if inner.alarm <= now { - inner.alarm = u64::MAX; - - Some((inner.callback, inner.ctx)) - } else { - None - } - } else { - panic!("Going back in time?"); - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); - } - } - } - - impl Driver for TestDriver { - fn now(&self) -> u64 { - self.0.lock().unwrap().now - } - - unsafe fn allocate_alarm(&self) -> Option { - Some(AlarmHandle::new(0)) - } - - fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - let mut inner = self.0.lock().unwrap(); - - inner.callback = callback; - inner.ctx = ctx; - } - - fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); - - if timestamp <= inner.now { - Some((inner.callback, inner.ctx)) - } else { - inner.alarm = timestamp; - None - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); - } - } - } - - struct TestWaker { - pub awoken: Rc>, - pub waker: Waker, - } - - impl TestWaker { - fn new() -> Self { - let flag = Rc::new(Cell::new(false)); - - const VTABLE: RawWakerVTable = RawWakerVTable::new( - |data: *const ()| { - unsafe { - Rc::increment_strong_count(data as *const Cell); - } - - RawWaker::new(data as _, &VTABLE) - }, - |data: *const ()| unsafe { - let data = data as *const Cell; - data.as_ref().unwrap().set(true); - Rc::decrement_strong_count(data); - }, - |data: *const ()| unsafe { - (data as *const Cell).as_ref().unwrap().set(true); - }, - |data: *const ()| unsafe { - Rc::decrement_strong_count(data); - }, - ); - - let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); - - Self { - awoken: flag.clone(), - waker: unsafe { Waker::from_raw(raw) }, - } - } - } - - // TODO: This impl should be part of `embassy-sync`, hidden behind the "std" feature gate - pub struct StdRawMutex(std::sync::Mutex<()>); - - unsafe impl RawMutex for StdRawMutex { - const INIT: Self = StdRawMutex(std::sync::Mutex::new(())); - - fn lock(&self, f: impl FnOnce() -> R) -> R { - let _guard = self.0.lock().unwrap(); - - f() - } - } - - const QUEUE_MAX_LEN: usize = 8; - - crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); - crate::generic_queue!(static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new()); - - fn setup() { - DRIVER.reset(); - - QUEUE.alarm.set(None); - QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); - QUEUE.inner.lock(|inner| { - *inner.borrow_mut() = InnerQueue::new(); - }); - - unsafe { super::initialize() }; - } - - fn queue_len() -> usize { - QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) - } - - #[test] - #[serial] - #[should_panic(expected = "Queue is not initialized yet")] - fn test_not_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - } - - #[test] - #[serial] - fn test_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - assert!(QUEUE.alarm.get().is_none()); - - unsafe { QUEUE.initialize() }; - - assert!(QUEUE.alarm.get().is_some()); - } - - #[test] - #[serial] - #[should_panic(expected = "Queue is already initialized")] - fn test_already_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - unsafe { QUEUE.initialize() }; - - assert!(QUEUE.alarm.get().is_some()); - - unsafe { QUEUE.initialize() }; - } - - #[test] - #[serial] - fn test_schedule() { - setup(); - - assert_eq!(queue_len(), 0); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert!(!waker.awoken.get()); - assert_eq!(queue_len(), 1); - } - - #[test] - #[serial] - fn test_schedule_same() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - assert_eq!(queue_len(), 1); - - let waker2 = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker2.waker); - - assert_eq!(queue_len(), 2); - } - - #[test] - #[serial] - fn test_trigger() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - assert!(!waker.awoken.get()); - - DRIVER.set_now(Instant::from_secs(99).as_ticks()); - - assert!(!waker.awoken.get()); - - assert_eq!(queue_len(), 1); - - DRIVER.set_now(Instant::from_secs(100).as_ticks()); - - assert!(waker.awoken.get()); - - assert_eq!(queue_len(), 0); - } - - #[test] - #[serial] - fn test_immediate_trigger() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - DRIVER.set_now(Instant::from_secs(50).as_ticks()); - - let waker2 = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(40), &waker2.waker); - - assert!(!waker.awoken.get()); - assert!(waker2.awoken.get()); - assert_eq!(queue_len(), 1); - } - - #[test] - #[serial] - fn test_queue_overflow() { - setup(); - - for i in 1..QUEUE_MAX_LEN { - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(310), &waker.waker); - - assert_eq!(queue_len(), i); - assert!(!waker.awoken.get()); - } - - let first_waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(300), &first_waker.waker); - - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(!first_waker.awoken.get()); - - let second_waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(305), &second_waker.waker); - - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(first_waker.awoken.get()); - - QUEUE.schedule(Instant::from_secs(320), &TestWaker::new().waker); - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(second_waker.awoken.get()); - } -} diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs new file mode 100644 index 00000000..1c4e5398 --- /dev/null +++ b/embassy-time/src/queue_generic.rs @@ -0,0 +1,474 @@ +use core::cell::RefCell; +use core::cmp::Ordering; +use core::task::Waker; + +use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; + +use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use crate::queue::TimerQueue; +use crate::Instant; + +#[cfg(feature = "generic-queue-8")] +const QUEUE_SIZE: usize = 8; +#[cfg(feature = "generic-queue-16")] +const QUEUE_SIZE: usize = 16; +#[cfg(feature = "generic-queue-32")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-64")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-128")] +const QUEUE_SIZE: usize = 128; +#[cfg(not(any( + feature = "generic-queue-8", + feature = "generic-queue-16", + feature = "generic-queue-32", + feature = "generic-queue-64", + feature = "generic-queue-128" +)))] +const QUEUE_SIZE: usize = 64; + +#[derive(Debug)] +struct Timer { + at: Instant, + waker: Waker, +} + +impl PartialEq for Timer { + fn eq(&self, other: &Self) -> bool { + self.at == other.at + } +} + +impl Eq for Timer {} + +impl PartialOrd for Timer { + fn partial_cmp(&self, other: &Self) -> Option { + self.at.partial_cmp(&other.at) + } +} + +impl Ord for Timer { + fn cmp(&self, other: &Self) -> Ordering { + self.at.cmp(&other.at) + } +} + +struct InnerQueue { + queue: SortedLinkedList, + alarm: Option, + alarm_at: Instant, +} + +impl InnerQueue { + const fn new() -> Self { + Self { + queue: SortedLinkedList::new_u8(), + alarm: None, + alarm_at: Instant::MAX, + } + } + + fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { + self.queue + .find_mut(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.at = at; + timer.finish(); + }) + .unwrap_or_else(|| { + let mut timer = Timer { + waker: waker.clone(), + at, + }; + + loop { + match self.queue.push(timer) { + Ok(()) => break, + Err(e) => timer = e, + } + + self.queue.pop().unwrap().waker.wake(); + } + }); + + // Don't wait for the alarm callback to trigger and directly + // dispatch all timers that are already due + // + // Then update the alarm if necessary + self.dispatch(alarm_schedule); + } + + fn dispatch(&mut self, alarm_schedule: &AtomicU64) { + let now = Instant::now(); + + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + self.update_alarm(alarm_schedule); + } + + fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { + if let Some(timer) = self.queue.peek() { + let new_at = timer.at; + + if self.alarm_at != new_at { + self.alarm_at = new_at; + alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); + } + } else { + self.alarm_at = Instant::MAX; + alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); + } + } + + fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { + self.alarm_at = Instant::MAX; + + self.dispatch(alarm_schedule); + } +} + +struct Queue { + inner: Mutex>, + alarm_schedule: AtomicU64, +} + +impl Queue { + const fn new() -> Self { + Self { + inner: Mutex::new(RefCell::new(InnerQueue::new())), + alarm_schedule: AtomicU64::new(u64::MAX), + } + } + + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + self.inner.lock(|inner| { + let mut inner = inner.borrow_mut(); + + if inner.alarm.is_none() { + let handle = unsafe { allocate_alarm() }.unwrap(); + inner.alarm = Some(handle); + + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); + } + + inner.schedule_wake(at, waker, &self.alarm_schedule) + }); + + self.update_alarm(); + } + + fn update_alarm(&self) { + // Need to set the alarm when we are *not* holding the mutex on the inner queue + // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately + // call us back if the timestamp is in the past. + let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); + + if alarm_at < u64::MAX { + set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at); + } + } + + fn handle_alarm(&self) { + self.inner + .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); + + self.update_alarm(); + } + + fn handle_alarm_callback(ctx: *mut ()) { + unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); + } +} + +impl TimerQueue for Queue { + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + Queue::schedule_wake(self, at, waker); + } +} + +crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); + +#[cfg(test)] +mod tests { + use core::cell::Cell; + use core::sync::atomic::Ordering; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use std::rc::Rc; + use std::sync::Mutex; + + use serial_test::serial; + + use super::InnerQueue; + use crate::driver::{AlarmHandle, Driver}; + use crate::queue_generic::QUEUE; + use crate::Instant; + + struct InnerTestDriver { + now: u64, + alarm: u64, + callback: fn(*mut ()), + ctx: *mut (), + } + + impl InnerTestDriver { + const fn new() -> Self { + Self { + now: 0, + alarm: u64::MAX, + callback: Self::noop, + ctx: core::ptr::null_mut(), + } + } + + fn noop(_ctx: *mut ()) {} + } + + unsafe impl Send for InnerTestDriver {} + + struct TestDriver(Mutex); + + impl TestDriver { + const fn new() -> Self { + Self(Mutex::new(InnerTestDriver::new())) + } + + fn reset(&self) { + *self.0.lock().unwrap() = InnerTestDriver::new(); + } + + fn set_now(&self, now: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if inner.now < now { + inner.now = now; + + if inner.alarm <= now { + inner.alarm = u64::MAX; + + Some((inner.callback, inner.ctx)) + } else { + None + } + } else { + panic!("Going back in time?"); + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + impl Driver for TestDriver { + fn now(&self) -> u64 { + self.0.lock().unwrap().now + } + + unsafe fn allocate_alarm(&self) -> Option { + Some(AlarmHandle::new(0)) + } + + fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + let mut inner = self.0.lock().unwrap(); + + inner.callback = callback; + inner.ctx = ctx; + } + + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + Some((inner.callback, inner.ctx)) + } else { + inner.alarm = timestamp; + None + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + struct TestWaker { + pub awoken: Rc>, + pub waker: Waker, + } + + impl TestWaker { + fn new() -> Self { + let flag = Rc::new(Cell::new(false)); + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + |data: *const ()| { + unsafe { + Rc::increment_strong_count(data as *const Cell); + } + + RawWaker::new(data as _, &VTABLE) + }, + |data: *const ()| unsafe { + let data = data as *const Cell; + data.as_ref().unwrap().set(true); + Rc::decrement_strong_count(data); + }, + |data: *const ()| unsafe { + (data as *const Cell).as_ref().unwrap().set(true); + }, + |data: *const ()| unsafe { + Rc::decrement_strong_count(data); + }, + ); + + let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); + + Self { + awoken: flag.clone(), + waker: unsafe { Waker::from_raw(raw) }, + } + } + } + + crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); + + fn setup() { + DRIVER.reset(); + + QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); + QUEUE.inner.lock(|inner| { + *inner.borrow_mut() = InnerQueue::new(); + }); + } + + fn queue_len() -> usize { + QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + } + + #[test] + #[serial] + fn test_schedule() { + setup(); + + assert_eq!(queue_len(), 0); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert!(!waker.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_schedule_same() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + assert_eq!(queue_len(), 1); + + let waker2 = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker2.waker); + + assert_eq!(queue_len(), 2); + } + + #[test] + #[serial] + fn test_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + assert!(!waker.awoken.get()); + + DRIVER.set_now(Instant::from_secs(99).as_ticks()); + + assert!(!waker.awoken.get()); + + assert_eq!(queue_len(), 1); + + DRIVER.set_now(Instant::from_secs(100).as_ticks()); + + assert!(waker.awoken.get()); + + assert_eq!(queue_len(), 0); + } + + #[test] + #[serial] + fn test_immediate_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + DRIVER.set_now(Instant::from_secs(50).as_ticks()); + + let waker2 = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(40), &waker2.waker); + + assert!(!waker.awoken.get()); + assert!(waker2.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_queue_overflow() { + setup(); + + for i in 1..super::QUEUE_SIZE { + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(310), &waker.waker); + + assert_eq!(queue_len(), i); + assert!(!waker.awoken.get()); + } + + let first_waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(300), &first_waker.waker); + + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(!first_waker.awoken.get()); + + let second_waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(305), &second_waker.waker); + + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(first_waker.awoken.get()); + + QUEUE.schedule_wake(Instant::from_secs(320), &TestWaker::new().waker); + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(second_waker.awoken.get()); + } +} From 4d5550070fe5e80ff2296a71239c568c774b9ceb Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 09:17:43 +0300 Subject: [PATCH 048/374] Change time Driver contract to never fire the alarm synchronously --- embassy-executor/src/raw/mod.rs | 78 +++++++++++++++++-------------- embassy-nrf/src/time_driver.rs | 19 ++++---- embassy-rp/src/timer.rs | 17 +++---- embassy-stm32/src/time_driver.rs | 18 +++---- embassy-time/src/driver.rs | 13 +++--- embassy-time/src/driver_std.rs | 4 +- embassy-time/src/driver_wasm.rs | 4 +- embassy-time/src/queue_generic.rs | 77 +++++++++++------------------- 8 files changed, 113 insertions(+), 117 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e1258ebb..5bcb1e6e 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -354,46 +354,54 @@ impl Executor { /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { - #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); + loop { + #[cfg(feature = "integrated-timers")] + self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); - self.run_queue.dequeue_all(|p| { - let task = p.as_ref(); + self.run_queue.dequeue_all(|p| { + let task = p.as_ref(); + + #[cfg(feature = "integrated-timers")] + task.expires_at.set(Instant::MAX); + + let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); + if state & STATE_SPAWNED == 0 { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + return; + } + + #[cfg(feature = "rtos-trace")] + trace::task_exec_begin(p.as_ptr() as u32); + + // Run the task + task.poll_fn.read()(p as _); + + #[cfg(feature = "rtos-trace")] + trace::task_exec_end(); + + // Enqueue or update into timer_queue + #[cfg(feature = "integrated-timers")] + self.timer_queue.update(p); + }); #[cfg(feature = "integrated-timers")] - task.expires_at.set(Instant::MAX); - - let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); - if state & STATE_SPAWNED == 0 { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - return; + { + // If this is already in the past, set_alarm might return false + // In that case do another poll loop iteration. + let next_expiration = self.timer_queue.next_expiration(); + if driver::set_alarm(self.alarm, next_expiration.as_ticks()) { + break; + } } - #[cfg(feature = "rtos-trace")] - trace::task_exec_begin(p.as_ptr() as u32); - - // Run the task - task.poll_fn.read()(p as _); - - #[cfg(feature = "rtos-trace")] - trace::task_exec_end(); - - // Enqueue or update into timer_queue - #[cfg(feature = "integrated-timers")] - self.timer_queue.update(p); - }); - - #[cfg(feature = "integrated-timers")] - { - // If this is already in the past, set_alarm will immediately trigger the alarm. - // This will cause `signal_fn` to be called, which will cause `poll()` to be called again, - // so we immediately do another poll loop iteration. - let next_expiration = self.timer_queue.next_expiration(); - driver::set_alarm(self.alarm, next_expiration.as_ticks()); + #[cfg(not(feature = "integrated-timers"))] + { + break; + } } #[cfg(feature = "rtos-trace")] diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index c32a4463..0d03ad52 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -243,20 +243,19 @@ impl Driver for RtcDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { + let t = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= t { + return false; + } + let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); - let t = self.now(); - - // If alarm timestamp has passed, trigger it instantly. - if timestamp <= t { - self.trigger_alarm(n, cs); - return; - } - let r = rtc(); // If it hasn't triggered yet, setup it in the compare channel. @@ -287,6 +286,8 @@ impl Driver for RtcDriver { // It will be setup later by `next_period`. r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); } + + true }) } } diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 5215c0c0..8f280f55 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -68,9 +68,16 @@ impl Driver for TimerDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { let n = alarm.id() as usize; critical_section::with(|cs| { + let now = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= now { + return false; + } + let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(timestamp); @@ -80,13 +87,7 @@ impl Driver for TimerDriver { // it is checked if the alarm time has passed. unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; - let now = self.now(); - - // If alarm timestamp has passed, trigger it instantly. - // This disarms it. - if timestamp <= now { - self.trigger_alarm(n, cs); - } + true }) } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ed3225c5..e4c266e7 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -292,21 +292,21 @@ impl Driver for RtcDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { + let t = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= t { + return false; + } + let r = T::regs_gp16(); let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); - let t = self.now(); - if timestamp <= t { - unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; - self.trigger_alarm(n, cs); - return; - } - let safe_timestamp = timestamp.max(t + 3); // Write the CCR value regardless of whether we're going to enable it now or not. @@ -317,6 +317,8 @@ impl Driver for RtcDriver { let diff = timestamp - t; // NOTE(unsafe) We're in a critical section unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) }; + + true }) } } diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs index 79ae14b9..5c2ad3b2 100644 --- a/embassy-time/src/driver.rs +++ b/embassy-time/src/driver.rs @@ -105,20 +105,21 @@ pub trait Driver: Send + Sync + 'static { /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm /// timestamp, the provided callback function will be called. /// - /// If `timestamp` is already in the past, the alarm callback must be immediately fired. - /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. + /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`. + /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set, + /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously. /// /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. /// /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; } extern "Rust" { fn _embassy_time_now() -> u64; fn _embassy_time_allocate_alarm() -> Option; fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); + fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; } /// See [`Driver::now`] @@ -139,7 +140,7 @@ pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ( } /// See [`Driver::set_alarm`] -pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { +pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { unsafe { _embassy_time_set_alarm(alarm, timestamp) } } @@ -167,7 +168,7 @@ macro_rules! time_driver_impl { } #[no_mangle] - fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) { + fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool { <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp) } }; diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 2ddb2e60..fc7fd197 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -127,12 +127,14 @@ impl Driver for TimeDriver { alarm.ctx = ctx; } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.timestamp = timestamp; unsafe { self.signaler.as_ref() }.signal(); + + true } } diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index e4497e6a..d7a6b0d8 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -81,13 +81,15 @@ impl Driver for TimeDriver { } } - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.closure.replace(Closure::new(move || { callback(ctx); })); + + true } fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 1c4e5398..83f73484 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -2,7 +2,6 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; @@ -71,7 +70,7 @@ impl InnerQueue { } } - fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { + fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { @@ -98,50 +97,54 @@ impl InnerQueue { // dispatch all timers that are already due // // Then update the alarm if necessary - self.dispatch(alarm_schedule); + self.dispatch(); } - fn dispatch(&mut self, alarm_schedule: &AtomicU64) { - let now = Instant::now(); + fn dispatch(&mut self) { + loop { + let now = Instant::now(); - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + if self.update_alarm() { + break; + } } - - self.update_alarm(alarm_schedule); } - fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { + fn update_alarm(&mut self) -> bool { if let Some(timer) = self.queue.peek() { let new_at = timer.at; if self.alarm_at != new_at { self.alarm_at = new_at; - alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); + + return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks()); } } else { self.alarm_at = Instant::MAX; - alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); } + + true } - fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { + fn handle_alarm(&mut self) { self.alarm_at = Instant::MAX; - self.dispatch(alarm_schedule); + self.dispatch(); } } struct Queue { inner: Mutex>, - alarm_schedule: AtomicU64, } impl Queue { const fn new() -> Self { Self { inner: Mutex::new(RefCell::new(InnerQueue::new())), - alarm_schedule: AtomicU64::new(u64::MAX), } } @@ -156,28 +159,12 @@ impl Queue { set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); } - inner.schedule_wake(at, waker, &self.alarm_schedule) + inner.schedule_wake(at, waker) }); - - self.update_alarm(); - } - - fn update_alarm(&self) { - // Need to set the alarm when we are *not* holding the mutex on the inner queue - // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately - // call us back if the timestamp is in the past. - let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - - if alarm_at < u64::MAX { - set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at); - } } fn handle_alarm(&self) { - self.inner - .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); - - self.update_alarm(); + self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); } fn handle_alarm_callback(ctx: *mut ()) { @@ -196,7 +183,6 @@ crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); #[cfg(test)] mod tests { use core::cell::Cell; - use core::sync::atomic::Ordering; use core::task::{RawWaker, RawWakerVTable, Waker}; use std::rc::Rc; use std::sync::Mutex; @@ -282,20 +268,14 @@ mod tests { inner.ctx = ctx; } - fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { + let mut inner = self.0.lock().unwrap(); - if timestamp <= inner.now { - Some((inner.callback, inner.ctx)) - } else { - inner.alarm = timestamp; - None - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); + if timestamp <= inner.now { + false + } else { + inner.alarm = timestamp; + true } } } @@ -344,7 +324,6 @@ mod tests { fn setup() { DRIVER.reset(); - QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); QUEUE.inner.lock(|inner| { *inner.borrow_mut() = InnerQueue::new(); }); From f78c706b89feed71a7e0a3eaf332f55813698c7f Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 11:10:59 +0300 Subject: [PATCH 049/374] Address review feedback --- embassy-nrf/src/time_driver.rs | 18 +++++++++++------- embassy-rp/src/timer.rs | 20 ++++++++++++-------- embassy-stm32/src/time_driver.rs | 18 +++++++++++------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 0d03ad52..bc2c8a3c 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -245,19 +245,23 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let t = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= t { - return false; - } - let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); let r = rtc(); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + + alarm.timestamp.set(u64::MAX); + + return false; + } + // If it hasn't triggered yet, setup it in the compare channel. // Write the CC value regardless of whether we're going to enable it now or not. diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 8f280f55..80efd779 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -71,13 +71,6 @@ impl Driver for TimerDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { let n = alarm.id() as usize; critical_section::with(|cs| { - let now = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= now { - return false; - } - let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(timestamp); @@ -87,7 +80,18 @@ impl Driver for TimerDriver { // it is checked if the alarm time has passed. unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; - true + let now = self.now(); + if timestamp <= now { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } + + alarm.timestamp.set(u64::MAX); + + false + } else { + true + } }) } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index e4c266e7..ab55cc08 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -294,19 +294,23 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let t = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= t { - return false; - } - let r = T::regs_gp16(); let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; + + alarm.timestamp.set(u64::MAX); + + return false; + } + let safe_timestamp = timestamp.max(t + 3); // Write the CCR value regardless of whether we're going to enable it now or not. From 4ce4131f8bad84ab38f595ac02dc5292611aaac0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 11:30:04 +0300 Subject: [PATCH 050/374] Implement i2cv1 timeout --- embassy-stm32/src/i2c/mod.rs | 3 + embassy-stm32/src/i2c/timeout.rs | 132 +++++++++++++++++++++++++++++++ embassy-stm32/src/i2c/v1.rs | 101 +++++++++++++++++------ examples/stm32f4/src/bin/i2c.rs | 30 +++++++ 4 files changed, 243 insertions(+), 23 deletions(-) create mode 100644 embassy-stm32/src/i2c/timeout.rs create mode 100644 examples/stm32f4/src/bin/i2c.rs diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 9d314f41..f4f64992 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,6 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +mod timeout; +pub use timeout::*; + use crate::peripherals; #[derive(Debug)] diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs new file mode 100644 index 00000000..12319af8 --- /dev/null +++ b/embassy-stm32/src/i2c/timeout.rs @@ -0,0 +1,132 @@ +use embassy_time::{Duration, Instant}; + +use super::{Error, I2c, Instance}; + +pub struct TimeoutI2c<'d, T: Instance> { + i2c: &'d mut I2c<'d, T>, + timeout: Duration, +} + +fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { + let deadline = Instant::now() + timeout; + move || { + if Instant::now() > deadline { + Err(Error::Timeout) + } else { + Ok(()) + } + } +} + +impl<'d, T: Instance> TimeoutI2c<'d, T> { + pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self { + Self { i2c, timeout } + } + + pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) + } + + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, self.timeout) + } + + pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) + } + + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, self.timeout) + } + + pub fn blocking_write_read_timeout( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + timeout: Duration, + ) -> Result<(), Error> { + self.i2c + .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) + } + + pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> { + type Error = Error; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, buffer) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, bytes) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> { + type Error = Error; + + fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, bytes, buffer) + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> { + 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, + { + todo!(); + } + + fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> + where + B: IntoIterator, + { + todo!(); + } + + 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::Operation<'a>], + ) -> Result<(), Self::Error> { + todo!(); + } + + fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + todo!(); + } + } +} diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f39a37df..92a89882 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -141,7 +141,12 @@ impl<'d, T: Instance> I2c<'d, T> { Ok(sr1) } - unsafe fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + unsafe fn write_bytes( + &mut self, + addr: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { // Send a START condition T::regs().cr1().modify(|reg| { @@ -149,7 +154,9 @@ impl<'d, T: Instance> I2c<'d, T> { }); // Wait until START condition was generated - while !self.check_and_clear_error_flags()?.start() {} + while !self.check_and_clear_error_flags()?.start() { + check_timeout()?; + } // Also wait until signalled we're master and everything is waiting for us while { @@ -157,7 +164,9 @@ impl<'d, T: Instance> I2c<'d, T> { let sr2 = T::regs().sr2().read(); !sr2.msl() && !sr2.busy() - } {} + } { + check_timeout()?; + } // Set up current address, we're trying to talk to T::regs().dr().write(|reg| reg.set_dr(addr << 1)); @@ -165,26 +174,30 @@ impl<'d, T: Instance> I2c<'d, T> { // Wait until address was sent // Wait for the address to be acknowledged // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. - while !self.check_and_clear_error_flags()?.addr() {} + while !self.check_and_clear_error_flags()?.addr() { + check_timeout()?; + } // Clear condition by reading SR2 let _ = T::regs().sr2().read(); // Send bytes for c in bytes { - self.send_byte(*c)?; + self.send_byte(*c, &check_timeout)?; } // Fallthrough is success Ok(()) } - unsafe fn send_byte(&self, byte: u8) -> Result<(), Error> { + unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. !self.check_and_clear_error_flags()?.txe() - } {} + } { + check_timeout()?; + } // Push out a byte of data T::regs().dr().write(|reg| reg.set_dr(byte)); @@ -193,24 +206,33 @@ impl<'d, T: Instance> I2c<'d, T> { while { // Check for any potential error conditions. !self.check_and_clear_error_flags()?.btf() - } {} + } { + check_timeout()?; + } Ok(()) } - unsafe fn recv_byte(&self) -> Result { + unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result { while { // Check for any potential error conditions. self.check_and_clear_error_flags()?; !T::regs().sr1().read().rxne() - } {} + } { + check_timeout()?; + } let value = T::regs().dr().read().dr(); Ok(value) } - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read_timeout( + &mut self, + addr: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { if let Some((last, buffer)) = buffer.split_last_mut() { // Send a START condition and set ACK bit unsafe { @@ -221,27 +243,33 @@ impl<'d, T: Instance> I2c<'d, T> { } // Wait until START condition was generated - while unsafe { !T::regs().sr1().read().start() } {} + while unsafe { !self.check_and_clear_error_flags()?.start() } { + check_timeout()?; + } // Also wait until signalled we're master and everything is waiting for us while { let sr2 = unsafe { T::regs().sr2().read() }; !sr2.msl() && !sr2.busy() - } {} + } { + check_timeout()?; + } // Set up current address, we're trying to talk to unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) } // Wait until address was sent // Wait for the address to be acknowledged - while unsafe { !self.check_and_clear_error_flags()?.addr() } {} + while unsafe { !self.check_and_clear_error_flags()?.addr() } { + check_timeout()?; + } // Clear condition by reading SR2 let _ = unsafe { T::regs().sr2().read() }; // Receive bytes into buffer for c in buffer { - *c = unsafe { self.recv_byte()? }; + *c = unsafe { self.recv_byte(&check_timeout)? }; } // Prepare to send NACK then STOP after next byte @@ -253,10 +281,12 @@ impl<'d, T: Instance> I2c<'d, T> { } // Receive last byte - *last = unsafe { self.recv_byte()? }; + *last = unsafe { self.recv_byte(&check_timeout)? }; // Wait for the STOP to be sent. - while unsafe { T::regs().cr1().read().stop() } {} + while unsafe { T::regs().cr1().read().stop() } { + check_timeout()?; + } // Fallthrough is success Ok(()) @@ -265,25 +295,50 @@ impl<'d, T: Instance> I2c<'d, T> { } } - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, || Ok(())) + } + + pub fn blocking_write_timeout( + &mut self, + addr: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { unsafe { - self.write_bytes(addr, bytes)?; + self.write_bytes(addr, bytes, &check_timeout)?; // Send a STOP condition T::regs().cr1().modify(|reg| reg.set_stop(true)); // Wait for STOP condition to transmit. - while T::regs().cr1().read().stop() {} + while T::regs().cr1().read().stop() { + check_timeout()?; + } }; // Fallthrough is success Ok(()) } - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - unsafe { self.write_bytes(addr, bytes)? }; - self.blocking_read(addr, buffer)?; + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, || Ok(())) + } + + pub fn blocking_write_read_timeout( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { + unsafe { self.write_bytes(addr, bytes, &check_timeout)? }; + self.blocking_read_timeout(addr, buffer, &check_timeout)?; Ok(()) } + + pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(())) + } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs new file mode 100644 index 00000000..99e3cecf --- /dev/null +++ b/examples/stm32f4/src/bin/i2c.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} From e3cf4255c6395ff5174432ab332189adf1ef431a Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 11:31:54 +0300 Subject: [PATCH 051/374] Help compiler with type inference --- embassy-stm32/src/time_driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ab55cc08..8e84570a 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -296,7 +296,7 @@ impl Driver for RtcDriver { critical_section::with(|cs| { let r = T::regs_gp16(); - let n = alarm.id() as _; + let n = alarm.id() as usize; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); From d99841fea90caccfd95c2dad8f233ab3198f7371 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 11:38:15 +0300 Subject: [PATCH 052/374] Implement time feature --- embassy-stm32/Cargo.toml | 5 ++++- embassy-stm32/src/i2c/mod.rs | 2 ++ embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/subghz/timeout.rs | 1 + embassy-stm32/src/subghz/tx_params.rs | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9194ae78..c598604b 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -82,9 +82,12 @@ memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] +# Enables additional driver features that depend on embassy-time +time = ["dep:embassy-time"] + # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. -_time-driver = ["dep:embassy-time"] +_time-driver = ["time"] time-driver-any = ["_time-driver"] time-driver-tim2 = ["_time-driver"] time-driver-tim3 = ["_time-driver"] diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f4f64992..f898fcc8 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,7 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +#[cfg(feature = "time")] mod timeout; +#[cfg(feature = "time")] pub use timeout::*; use crate::peripherals; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0392e808..bcf2feee 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -52,7 +52,7 @@ pub mod sdmmc; pub mod spi; #[cfg(usart)] pub mod usart; -#[cfg(usb)] +#[cfg(all(usb, feature = "time"))] pub mod usb; #[cfg(any(otgfs, otghs))] pub mod usb_otg; diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs index 28b3b0c2..0ae49dd9 100644 --- a/embassy-stm32/src/subghz/timeout.rs +++ b/embassy-stm32/src/subghz/timeout.rs @@ -439,6 +439,7 @@ impl From for [u8; 3] { } } +#[cfg(feature = "time")] impl From for embassy_time::Duration { fn from(to: Timeout) -> Self { embassy_time::Duration::from_micros(to.as_micros().into()) diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs index cede6f2c..03bdb1ea 100644 --- a/embassy-stm32/src/subghz/tx_params.rs +++ b/embassy-stm32/src/subghz/tx_params.rs @@ -44,6 +44,7 @@ impl From for core::time::Duration { } } +#[cfg(feature = "time")] impl From for embassy_time::Duration { fn from(rt: RampTime) -> Self { match rt { From 545cc9326b47efc27549a60b3539e93ea0d04d70 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 23 Oct 2022 16:31:10 -0500 Subject: [PATCH 053/374] stm32/adc: Remove voltage and temperature conversions --- embassy-stm32/src/adc/f1.rs | 21 ------------------ embassy-stm32/src/adc/v2.rs | 23 ------------------- embassy-stm32/src/adc/v3.rs | 39 --------------------------------- embassy-stm32/src/adc/v4.rs | 14 ------------ examples/stm32f1/src/bin/adc.rs | 12 ++++++++-- examples/stm32f4/src/bin/adc.rs | 27 ++++++++++++++++++++--- examples/stm32f7/src/bin/adc.rs | 12 +++++++++- 7 files changed, 45 insertions(+), 103 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 50d4f9bf..c5b317ce 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -86,7 +86,6 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - calibrated_vdda: u32, phantom: PhantomData<&'d mut T>, } @@ -122,7 +121,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), - calibrated_vdda: VDDA_CALIB_MV, phantom: PhantomData, } } @@ -162,29 +160,10 @@ impl<'d, T: Instance> Adc<'d, T> { Temperature {} } - /// Calculates the system VDDA by sampling the internal VREF channel and comparing - /// to the expected value. If the chip's VDDA is not stable, run this before each ADC - /// conversion. - pub fn calibrate(&mut self, vref: &mut Vref) -> u32 { - let old_sample_time = self.sample_time; - self.sample_time = SampleTime::Cycles239_5; - - let vref_samp = self.read(vref); - self.sample_time = old_sample_time; - - self.calibrated_vdda = (ADC_MAX * VREF_INT) / u32::from(vref_samp); - self.calibrated_vdda - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.calibrated_vdda) / ADC_MAX) as u16 - } - /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 4fe4ad1f..53419c7f 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -80,15 +80,6 @@ impl super::sealed::InternalChannel for Temperature { } impl Temperature { - /// Converts temperature sensor reading in millivolts to degrees celcius - pub fn to_celcius(sample_mv: u16) -> f32 { - // From 6.3.22 Temperature sensor characteristics - const V25: i32 = 760; // mV - const AVG_SLOPE: f32 = 2.5; // mV/C - - (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 - } - /// Time needed for temperature sensor readings to stabilize pub fn start_time_us() -> u32 { 10 @@ -172,7 +163,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -200,7 +190,6 @@ where Self { sample_time: Default::default(), resolution: Resolution::default(), - vref_mv: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -213,18 +202,6 @@ where self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /// Enables internal voltage reference and returns [VrefInt], which can be used in /// [Adc::read_internal()] to perform conversion. pub fn enable_vrefint(&self) -> VrefInt { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 0f109088..816feeac 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -205,7 +205,6 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -244,7 +243,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), resolution: Resolution::default(), - vref_mv: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -285,31 +283,6 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat {} } - /// Calculates the system VDDA by sampling the internal VREFINT channel and comparing - /// the result with the value stored at the factory. If the chip's VDDA is not stable, run - /// this before each ADC conversion. - #[cfg(not(stm32g0))] // TODO is this supposed to be public? - #[allow(unused)] // TODO is this supposed to be public? - fn calibrate(&mut self, vrefint: &mut VrefInt) { - #[cfg(stm32l5)] - let vrefint_cal: u32 = todo!(); - #[cfg(not(stm32l5))] - let vrefint_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() }; - let old_sample_time = self.sample_time; - - // "Table 24. Embedded internal voltage reference" states that the sample time needs to be - // at a minimum 4 us. With 640.5 ADC cycles we have a minimum of 8 us at 80 MHz, leaving - // some headroom. - self.sample_time = SampleTime::Cycles640_5; - - // This can't actually fail, it's just in a result to satisfy hal trait - let vrefint_samp = self.read(vrefint); - - self.sample_time = old_sample_time; - - self.vref_mv = (VREF_CALIB_MV * u32::from(vrefint_cal)) / u32::from(vrefint_samp); - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } @@ -318,18 +291,6 @@ impl<'d, T: Instance> Adc<'d, T> { self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /* /// Convert a raw sample from the `Temperature` to deg C pub fn to_degrees_centigrade(sample: u16) -> f32 { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index eda2b2a7..2b8f1053 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -314,7 +314,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -352,7 +351,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { let mut s = Self { sample_time: Default::default(), - vref_mv: VREF_DEFAULT_MV, resolution: Resolution::default(), phantom: PhantomData, }; @@ -459,18 +457,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 2d6b4a0e..3521d06b 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -17,10 +17,18 @@ async fn main(_spawner: Spawner) { let mut pin = p.PB1; let mut vref = adc.enable_vref(&mut Delay); - adc.calibrate(&mut vref); + let vref_sample = adc.read(&mut vref); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREF_MV: u32 = 1200; + + (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + }; + loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 1d030f7d..5e036bb4 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,19 +24,40 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); + let vref_sample = adc.read_internal(&mut vrefint); + + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00071990.pdf + // 6.3.24 Reference voltage + const VREF_MILLIVOLTS: u32 = 1210; // mV + + (u32::from(sample) * VREF_MILLIVOLTS / u32::from(vref_sample)) as u16 + }; + + let convert_to_celcius = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00071990.pdf + // 6.3.22 Temperature sensor characteristics + const V25: i32 = 760; // mV + const AVG_SLOPE: f32 = 2.5; // mV/C + + let sample_mv = convert_to_millivolts(sample) as i32; + + (sample_mv - V25) as f32 / AVG_SLOPE + 25.0 + }; + loop { // Read pin let v = adc.read(&mut pin); - info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); + info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); // Read internal temperature let v = adc.read_internal(&mut temp); - let celcius = Temperature::to_celcius(adc.to_millivolts(v)); + let celcius = convert_to_celcius(v); info!("Internal temp: {} ({} C)", v, celcius); // Read internal voltage reference let v = adc.read_internal(&mut vrefint); - info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); + info!("VrefInt: {} ({} mV)", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 80fad8c4..d932f8b3 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,9 +16,19 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PA3; + let mut vref = adc.enable_vrefint(); + let vref_sample = adc.read_internal(&mut vref); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00273119.pdf + // 6.3.27 Reference voltage + const VREF_MV: u32 = 1210; + + (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + }; + loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } From 516f4ce94684d9b3f9310c9972f878dbf883600c Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 12:15:53 +0300 Subject: [PATCH 054/374] Fix embassy-time wasm build and fix a bug in wasm time driver --- embassy-time/src/driver_wasm.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index d7a6b0d8..63d04989 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -81,26 +81,32 @@ impl Driver for TimeDriver { } } - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool { + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.closure.replace(Closure::new(move || { callback(ctx); })); - - true } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; - let timeout = (timestamp - self.now()) as u32; if let Some(token) = alarm.token { clearTimeout(token); } - alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + + let now = self.now(); + if timestamp <= now { + false + } else { + let timeout = (timestamp - now) as u32; + alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + + true + } } } From 1bed02296cf15013e0149b36c3ddedb3278e9b88 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:33:17 +0300 Subject: [PATCH 055/374] i2cv2 timeouts --- embassy-stm32/src/i2c/v2.rs | 204 ++++++++++++++++++++++---------- examples/stm32h7/src/bin/i2c.rs | 41 +++++++ 2 files changed, 182 insertions(+), 63 deletions(-) create mode 100644 examples/stm32h7/src/bin/i2c.rs diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 89b52da9..40413b69 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -147,14 +147,23 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { + unsafe fn master_read( + address: u8, + length: usize, + stop: Stop, + reload: bool, + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256); if !restart { // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() {} + while T::regs().cr2().read().start() { + check_timeout()?; + } } // Set START and prepare to receive bytes into @@ -176,15 +185,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_autoend(stop.autoend()); w.set_reload(reload); }); + + Ok(()) } - unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) { + unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() {} + while T::regs().cr2().read().start() { + check_timeout()?; + } let reload = if reload { i2c::vals::Reload::NOTCOMPLETED @@ -204,12 +217,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_autoend(stop.autoend()); w.set_reload(reload); }); + + Ok(()) } - unsafe fn master_continue(length: usize, reload: bool) { + unsafe fn master_continue(length: usize, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { assert!(length < 256 && length > 0); - while !T::regs().isr().read().tcr() {} + while !T::regs().isr().read().tcr() { + check_timeout()?; + } let reload = if reload { i2c::vals::Reload::NOTCOMPLETED @@ -221,6 +238,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_nbytes(length as u8); w.set_reload(reload); }); + + Ok(()) } fn flush_txdr(&self) { @@ -243,7 +262,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { //} } - fn wait_txe(&self) -> Result<(), Error> { + fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -261,10 +280,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn wait_rxne(&self) -> Result<(), Error> { + fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -282,10 +303,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn wait_tc(&self) -> Result<(), Error> { + fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -303,10 +326,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> { + fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { let completed_chunks = buffer.len() / 255; let total_chunks = if completed_chunks * 255 == buffer.len() { completed_chunks @@ -322,20 +347,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - ); + &check_timeout + )?; } for (number, chunk) in buffer.chunks_mut(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx); + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } } for byte in chunk { // Wait until we have received something - self.wait_rxne()?; + self.wait_rxne(&check_timeout)?; unsafe { *byte = T::regs().rxdr().read().rxdata(); @@ -345,7 +371,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { let completed_chunks = bytes.len() / 255; let total_chunks = if completed_chunks * 255 == bytes.len() { completed_chunks @@ -359,14 +385,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ST SAD+W // NOTE(unsafe) We have &mut self unsafe { - Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0); + Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout)?; } for (number, chunk) in bytes.chunks(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx); + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } } @@ -374,7 +400,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe()?; + self.wait_txe(&check_timeout)?; unsafe { T::regs().txdr().write(|w| w.set_txdata(*byte)); @@ -382,7 +408,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc()?; + self.wait_tc(&check_timeout)?; if send_stop { self.master_stop(); @@ -396,6 +422,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { bytes: &[u8], first_slice: bool, last_slice: bool, + check_timeout: impl Fn() -> Result<(), Error> ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -447,11 +474,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_chunks != 1) || !last_slice, - ); + &check_timeout + )?; } } else { unsafe { - Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice); + Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?; T::regs().cr1().modify(|w| w.set_tcie(true)); } } @@ -461,32 +489,34 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); if chunks_transferred == total_chunks { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } else if chunks_transferred != 0 { remaining_len = remaining_len.saturating_sub(255); let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers unsafe { - Self::master_continue(remaining_len.min(255), !last_piece); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)); + } T::regs().cr1().modify(|w| w.set_tcie(true)); } } Poll::Pending }) - .await; + .await?; dma_transfer.await; if last_slice { // This should be done already - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); } Ok(()) } - async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> + async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { @@ -527,7 +557,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart); + Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart, &check_timeout)?; } poll_fn(|cx| { @@ -535,25 +565,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); if chunks_transferred == total_chunks { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } else if chunks_transferred != 0 { remaining_len = remaining_len.saturating_sub(255); let last_piece = chunks_transferred + 1 == total_chunks; // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_continue(remaining_len.min(255), !last_piece); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)) + } T::regs().cr1().modify(|w| w.set_tcie(true)); } } Poll::Pending }) - .await; + .await?; dma_transfer.await; // This should be done already - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); Ok(()) } @@ -561,18 +593,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + pub async fn write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { if bytes.is_empty() { - self.write_internal(address, bytes, true) + self.write_internal(address, bytes, true, &check_timeout) } else { - self.write_dma_internal(address, bytes, true, true).await + self.write_dma_internal(address, bytes, true, true, &check_timeout).await } } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, bytes, || Ok(())).await + } + + pub async fn write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -587,63 +626,97 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last).await?; + self.write_dma_internal(address, c, first, is_last, &check_timeout).await?; first = false; current = next; } Ok(()) } - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, bytes, || Ok(())).await + } + + pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { if buffer.is_empty() { - self.read_internal(address, buffer, false) + self.read_internal(address, buffer, false, &check_timeout) } else { - self.read_dma_internal(address, buffer, false).await + self.read_dma_internal(address, buffer, false, &check_timeout).await } } + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, || Ok(())).await + } + + pub async fn write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + if bytes.is_empty() { + self.write_internal(address, bytes, false, &check_timeout)?; + } else { + self.write_dma_internal(address, bytes, true, true, &check_timeout).await?; + } + + if buffer.is_empty() { + self.read_internal(address, buffer, true, &check_timeout)?; + } else { + self.read_dma_internal(address, buffer, true, &check_timeout).await?; + } + + Ok(()) + } + pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, false)?; - } else { - self.write_dma_internal(address, bytes, true, true).await?; - } - - if buffer.is_empty() { - self.read_internal(address, buffer, true)?; - } else { - self.read_dma_internal(address, buffer, true).await?; - } - - Ok(()) + self.write_read_timeout(address, bytes, buffer, || Ok(())).await } // ========================= // Blocking public API - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.read_internal(address, buffer, false) + pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + self.read_internal(address, buffer, false, &check_timeout) // Automatic Stop } + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(address, buffer, || Ok(())) + } + + pub fn blocking_write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + self.write_internal(address, bytes, true, &check_timeout) + } + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - self.write_internal(address, bytes, true) + self.blocking_write_timeout(address, bytes, || Ok(())) } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.write_internal(address, bytes, false)?; - self.read_internal(address, buffer, true) + pub fn blocking_write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + self.write_internal(address, bytes, false, &check_timeout)?; + self.read_internal(address, buffer, true, &check_timeout) // Automatic Stop } - pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) + } + + pub fn blocking_write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { if bytes.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -657,7 +730,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), - ); + &check_timeout + )?; } for (idx, slice) in bytes.iter().enumerate() { @@ -673,7 +747,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if idx != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255)); + Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), &check_timeout)?; } } @@ -681,7 +755,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index)); + Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), &check_timeout)?; } } @@ -689,7 +763,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe()?; + self.wait_txe(&check_timeout)?; // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); @@ -700,11 +774,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); Ok(()) } + + pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { + self.blocking_write_vectored_timeout(address, bytes, || Ok(())) + } } mod eh02 { diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs new file mode 100644 index 00000000..7a314b99 --- /dev/null +++ b/examples/stm32h7/src/bin/i2c.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + p.DMA1_CH4, + p.DMA1_CH5, + Hertz(100_000), + Default::default(), + ); + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} From 5f02bee388c1754a95e5cab95338aef9c5605c9a Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:34:55 +0300 Subject: [PATCH 056/374] Gate TimeoutI2c behind i2cv1 --- embassy-stm32/src/i2c/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f898fcc8..1dffbd10 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,9 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; -#[cfg(feature = "time")] +#[cfg(all(i2c_v1, feature = "time"))] mod timeout; -#[cfg(feature = "time")] +#[cfg(all(i2c_v1, feature = "time"))] pub use timeout::*; use crate::peripherals; From 9b209ffe1c9048f296213c7050919ccbcd7ded1b Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:39:47 +0300 Subject: [PATCH 057/374] Add docs --- embassy-stm32/src/i2c/timeout.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 12319af8..c4c035b4 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -2,6 +2,7 @@ use embassy_time::{Duration, Instant}; use super::{Error, I2c, Instance}; +/// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. pub struct TimeoutI2c<'d, T: Instance> { i2c: &'d mut I2c<'d, T>, timeout: Duration, @@ -23,22 +24,27 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { Self { i2c, timeout } } + /// Blocking read with a custom timeout pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) } + /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { self.blocking_read_timeout(addr, buffer, self.timeout) } + /// Blocking write with a custom timeout pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) } + /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { self.blocking_write_timeout(addr, bytes, self.timeout) } + /// Blocking write-read with a custom timeout pub fn blocking_write_read_timeout( &mut self, addr: u8, @@ -50,6 +56,7 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) } + /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) } From ad0eb3f4bd2b124fcb7cda6d6bd88e2b12632ea7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 24 Oct 2022 12:17:22 +0200 Subject: [PATCH 058/374] Implement flash padding to 256 under assumption that all QSPI NOR flashes are MultiwriteNorFlashes --- embassy-rp/src/flash.rs | 56 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 1ff2fd19..7a26085b 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,5 +1,6 @@ use embedded_storage::nor_flash::{ - check_erase, check_read, check_write, ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, + check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, + ReadNorFlash, }; pub const FLASH_BASE: usize = 0x10000000; @@ -9,7 +10,8 @@ pub const FLASH_BASE: usize = 0x10000000; // These limitations are currently enforced because of using the // RP2040 boot-rom flash functions, that are optimized for flash compatibility // rather than performance. -pub const WRITE_SIZE: usize = 256; +pub const PAGE_SIZE: usize = 256; +pub const WRITE_SIZE: usize = 1; pub const READ_SIZE: usize = 1; pub const ERASE_SIZE: usize = 4096; @@ -100,6 +102,8 @@ impl ReadNorFlash for Flash { } } +impl MultiwriteNorFlash for Flash {} + impl NorFlash for Flash { const WRITE_SIZE: usize = WRITE_SIZE; @@ -120,12 +124,58 @@ impl NorFlash for Flash { trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(offset, bytes, true)) }; + let end_offset = offset as usize + bytes.len(); + + let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE); + let start_padding = core::cmp::min(padded_offset, bytes.len()); + + // Pad in the beginning + if start_padding > 0 { + let start = PAGE_SIZE - padded_offset; + let end = start + start_padding; + + let mut pad_buf = [0xFF_u8; PAGE_SIZE]; + pad_buf[start..end].copy_from_slice(&bytes[..start_padding]); + + let unaligned_offset = offset as usize - start; + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + } + + let remaining_len = bytes.len() - start_padding; + let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE); + + // Write aligned slice of length in multiples of 256 bytes + // If the remaining bytes to be written is more than a full page. + if remaining_len >= PAGE_SIZE { + let aligned_data = &bytes[start_padding..end_padding]; + + let aligned_offset = if start_padding > 0 { + offset as usize + padded_offset + } else { + offset as usize + }; + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + } + + // Pad in the end + let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE); + let rem_padding = remaining_len % PAGE_SIZE; + if rem_padding > 0 { + let mut pad_buf = [0xFF_u8; PAGE_SIZE]; + pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]); + + let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + } Ok(()) } } +#[allow(dead_code)] mod ram_helpers { use core::marker::PhantomData; From ca8afacfd07b24c27441f5a4ea1d66719fd50038 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:11:15 +0300 Subject: [PATCH 059/374] Implement TimeoutI2c for i2cv2 --- embassy-stm32/src/i2c/mod.rs | 4 ++-- embassy-stm32/src/i2c/timeout.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 1dffbd10..f898fcc8 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,9 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; -#[cfg(all(i2c_v1, feature = "time"))] +#[cfg(feature = "time")] mod timeout; -#[cfg(all(i2c_v1, feature = "time"))] +#[cfg(feature = "time")] pub use timeout::*; use crate::peripherals; diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index c4c035b4..fd9a753a 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -3,8 +3,8 @@ use embassy_time::{Duration, Instant}; use super::{Error, I2c, Instance}; /// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. -pub struct TimeoutI2c<'d, T: Instance> { - i2c: &'d mut I2c<'d, T>, +pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { + i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, } @@ -19,8 +19,8 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'d, T: Instance> TimeoutI2c<'d, T> { - pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self { +impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { + pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { Self { i2c, timeout } } @@ -62,7 +62,7 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -70,7 +70,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -78,7 +78,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -90,11 +90,11 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<' mod eh1 { use super::*; - impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> { + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> { + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From 6062978d58915e1d0c7db103365f0048f836babc Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:22:20 +0300 Subject: [PATCH 060/374] Remove weird async timeouts --- embassy-stm32/src/i2c/v2.rs | 73 +++++++++++-------------------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 40413b69..876f6223 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -593,25 +593,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - if bytes.is_empty() { - self.write_internal(address, bytes, true, &check_timeout) - } else { - self.write_dma_internal(address, bytes, true, true, &check_timeout).await - } - } - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - self.write_timeout(address, bytes, || Ok(())).await + if bytes.is_empty() { + self.write_internal(address, bytes, true, || Ok(())) + } else { + self.write_dma_internal(address, bytes, true, true, || Ok(())).await + } } - pub async fn write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -626,56 +619,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last, &check_timeout).await?; + self.write_dma_internal(address, c, first, is_last, || Ok(())).await?; first = false; current = next; } Ok(()) } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - self.write_vectored_timeout(address, bytes, || Ok(())).await - } - - pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - if buffer.is_empty() { - self.read_internal(address, buffer, false, &check_timeout) - } else { - self.read_dma_internal(address, buffer, false, &check_timeout).await - } - } - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { - self.read_timeout(address, buffer, || Ok(())).await - } - - pub async fn write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - TXDMA: super::TxDma, - RXDMA: super::RxDma, - { - if bytes.is_empty() { - self.write_internal(address, bytes, false, &check_timeout)?; - } else { - self.write_dma_internal(address, bytes, true, true, &check_timeout).await?; - } - if buffer.is_empty() { - self.read_internal(address, buffer, true, &check_timeout)?; + self.read_internal(address, buffer, false, || Ok(())) } else { - self.read_dma_internal(address, buffer, true, &check_timeout).await?; + self.read_dma_internal(address, buffer, false, || Ok(())).await } - - Ok(()) } pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> @@ -683,7 +642,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { TXDMA: super::TxDma, RXDMA: super::RxDma, { - self.write_read_timeout(address, bytes, buffer, || Ok(())).await + if bytes.is_empty() { + self.write_internal(address, bytes, false, || Ok(()))?; + } else { + self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; + } + + if buffer.is_empty() { + self.read_internal(address, buffer, true, || Ok(()))?; + } else { + self.read_dma_internal(address, buffer, true, || Ok(())).await?; + } + + Ok(()) } // ========================= From 33f75419e542ef52d7d6a1403c9e3dbfd1c39abe Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:34:10 +0300 Subject: [PATCH 061/374] Unify i2cv1 definition with i2cv2 --- embassy-stm32/src/i2c/v1.rs | 22 +++++++++++++++++----- examples/stm32f4/src/bin/i2c.rs | 14 +++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 92a89882..f140e2b0 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,8 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::into_ref; +use embassy_hal_common::{into_ref, PeripheralRef}; +use crate::dma::NoDma; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; @@ -34,19 +35,26 @@ impl State { } } -pub struct I2c<'d, T: Instance> { +pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { phantom: PhantomData<&'d mut T>, + #[allow(dead_code)] + tx_dma: PeripheralRef<'d, TXDMA>, + #[allow(dead_code)] + rx_dma: PeripheralRef<'d, RXDMA>, } -impl<'d, T: Instance> I2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 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, freq: Hertz, config: Config, ) -> Self { - into_ref!(scl, sda); + into_ref!(scl, sda, tx_dma, rx_dma); T::enable(); T::reset(); @@ -99,7 +107,11 @@ impl<'d, T: Instance> I2c<'d, T> { }); } - Self { phantom: PhantomData } + Self { + phantom: PhantomData, + tx_dma, + rx_dma, + } } unsafe fn check_and_clear_error_flags(&self) -> Result { diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 99e3cecf..12965d2b 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -4,7 +4,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +19,17 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + NoDma, + NoDma, + Hertz(100_000), + Default::default(), + ); let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; From ac61e0ee9fcec2792c73806afe4c998019b0db75 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:39:13 +0300 Subject: [PATCH 062/374] fmt --- embassy-stm32/src/i2c/v2.rs | 106 ++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 876f6223..aa4e6bb0 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -189,7 +189,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + unsafe fn master_write( + address: u8, + length: usize, + stop: Stop, + reload: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end @@ -221,7 +227,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_continue(length: usize, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + unsafe fn master_continue( + length: usize, + reload: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256 && length > 0); while !T::regs().isr().read().tcr() { @@ -331,7 +341,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn read_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { let completed_chunks = buffer.len() / 255; let total_chunks = if completed_chunks * 255 == buffer.len() { completed_chunks @@ -347,7 +363,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - &check_timeout + &check_timeout, )?; } @@ -371,7 +387,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn write_internal( + &mut self, + address: u8, + bytes: &[u8], + send_stop: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { let completed_chunks = bytes.len() / 255; let total_chunks = if completed_chunks * 255 == bytes.len() { completed_chunks @@ -385,7 +407,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ST SAD+W // NOTE(unsafe) We have &mut self unsafe { - Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout)?; + Self::master_write( + address, + bytes.len().min(255), + Stop::Software, + last_chunk_idx != 0, + &check_timeout, + )?; } for (number, chunk) in bytes.chunks(255).enumerate() { @@ -422,7 +450,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { bytes: &[u8], first_slice: bool, last_slice: bool, - check_timeout: impl Fn() -> Result<(), Error> + check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -474,7 +502,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_chunks != 1) || !last_slice, - &check_timeout + &check_timeout, )?; } } else { @@ -516,7 +544,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + async fn read_dma_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { @@ -557,7 +591,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart, &check_timeout)?; + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_chunks != 1, + restart, + &check_timeout, + )?; } poll_fn(|cx| { @@ -573,7 +614,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { - return Poll::Ready(Err(e)) + return Poll::Ready(Err(e)); } T::regs().cr1().modify(|w| w.set_tcie(true)); } @@ -660,7 +701,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Blocking public API - pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_read_timeout( + &mut self, + address: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { self.read_internal(address, buffer, false, &check_timeout) // Automatic Stop } @@ -669,7 +715,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_read_timeout(address, buffer, || Ok(())) } - pub fn blocking_write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_write_timeout( + &mut self, + address: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { self.write_internal(address, bytes, true, &check_timeout) } @@ -677,7 +728,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_write_timeout(address, bytes, || Ok(())) } - pub fn blocking_write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_write_read_timeout( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { self.write_internal(address, bytes, false, &check_timeout)?; self.read_internal(address, buffer, true, &check_timeout) // Automatic Stop @@ -687,7 +744,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) } - pub fn blocking_write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_write_vectored_timeout( + &mut self, + address: u8, + bytes: &[&[u8]], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { if bytes.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -701,7 +763,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), - &check_timeout + &check_timeout, )?; } @@ -718,7 +780,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if idx != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), &check_timeout)?; + Self::master_continue( + slice_len.min(255), + (idx != last_slice_index) || (slice_len > 255), + &check_timeout, + )?; } } @@ -726,7 +792,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), &check_timeout)?; + Self::master_continue( + chunk.len(), + (number != last_chunk_idx) || (idx != last_slice_index), + &check_timeout, + )?; } } From 52c03cf0a4ae5a7a6374e6acac123670b83860fe Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:48:40 +0300 Subject: [PATCH 063/374] Add more docs --- embassy-stm32/src/i2c/timeout.rs | 3 +++ examples/stm32f4/src/bin/i2c.rs | 3 +++ examples/stm32h7/src/bin/i2c.rs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index fd9a753a..4fca1ca2 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -3,6 +3,9 @@ use embassy_time::{Duration, Instant}; use super::{Error, I2c, Instance}; /// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. +/// +/// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. +/// A regular [I2c] would freeze until condition is removed. pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 12965d2b..6e51c211 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -30,6 +30,9 @@ async fn main(_spawner: Spawner) -> ! { Hertz(100_000), Default::default(), ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index 7a314b99..d44319ae 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs @@ -29,6 +29,9 @@ async fn main(_spawner: Spawner) -> ! { Hertz(100_000), Default::default(), ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; From 7a6732adcfd09d72f5335f85cbe4e263234849e7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Mon, 24 Oct 2022 15:01:16 -0500 Subject: [PATCH 064/374] Improve examples --- examples/stm32f1/src/bin/adc.rs | 8 ++++---- examples/stm32f4/src/bin/adc.rs | 12 ++++++++---- examples/stm32f7/src/bin/adc.rs | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 3521d06b..ed59e279 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -16,14 +16,14 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PB1; - let mut vref = adc.enable_vref(&mut Delay); - let vref_sample = adc.read(&mut vref); + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/CD00161566.pdf // 5.3.4 Embedded reference voltage - const VREF_MV: u32 = 1200; + const VREFINT_MV: u32 = 1200; // mV - (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; loop { diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5e036bb4..1c9a0b35 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,14 +24,14 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); - let vref_sample = adc.read_internal(&mut vrefint); + let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00071990.pdf // 6.3.24 Reference voltage - const VREF_MILLIVOLTS: u32 = 1210; // mV + const VREFINT_MV: u32 = 1210; // mV - (u32::from(sample) * VREF_MILLIVOLTS / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; let convert_to_celcius = |sample| { @@ -45,6 +45,10 @@ async fn main(_spawner: Spawner) { (sample_mv - V25) as f32 / AVG_SLOPE + 25.0 }; + info!("VrefInt: {}", vrefint_sample); + const MAX_ADC_SAMPLE: u16 = (1 << 12) - 1; + info!("VCCA: {} mV", convert_to_millivolts(MAX_ADC_SAMPLE)); + loop { // Read pin let v = adc.read(&mut pin); @@ -57,7 +61,7 @@ async fn main(_spawner: Spawner) { // Read internal voltage reference let v = adc.read_internal(&mut vrefint); - info!("VrefInt: {} ({} mV)", v, convert_to_millivolts(v)); + info!("VrefInt: {}", v); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index d932f8b3..70b3b2a7 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,14 +16,14 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PA3; - let mut vref = adc.enable_vrefint(); - let vref_sample = adc.read_internal(&mut vref); + let mut vrefint = adc.enable_vrefint(); + let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00273119.pdf // 6.3.27 Reference voltage - const VREF_MV: u32 = 1210; + const VREFINT_MV: u32 = 1210; // mV - (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; loop { From ea868920e6859aac924a6266968f790a76d6b160 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 26 Oct 2022 09:13:26 +0200 Subject: [PATCH 065/374] Update nrf pacs --- embassy-nrf/Cargo.toml | 20 ++++++++++---------- embassy-nrf/src/chips/nrf52805.rs | 4 ++-- embassy-nrf/src/chips/nrf52810.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52811.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52832.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52833.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52840.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf5340_app.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf9160.rs | 16 ++++++++-------- 9 files changed, 68 insertions(+), 68 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5459bc90..dca5fd1b 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -90,13 +90,13 @@ embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } cfg-if = "1.0.0" -nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52811-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52820-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52832-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52833-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52840-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf5340-app-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf5340-net-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf9160-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } +nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index d078fa0a..dec31a84 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -193,8 +193,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 3e500098..e57a4a38 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -211,14 +211,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 25c7c0d9..918404cf 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -212,14 +212,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 18b8eda6..81e66c19 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -225,14 +225,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 3b33907d..92499e3c 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -271,14 +271,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index ae59f8b2..4beadfba 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -276,14 +276,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index edf800ef..7845d4a8 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -458,14 +458,14 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -impl_saadc_input!(P0_13, ANALOGINPUT0); -impl_saadc_input!(P0_14, ANALOGINPUT1); -impl_saadc_input!(P0_15, ANALOGINPUT2); -impl_saadc_input!(P0_16, ANALOGINPUT3); -impl_saadc_input!(P0_17, ANALOGINPUT4); -impl_saadc_input!(P0_18, ANALOGINPUT5); -impl_saadc_input!(P0_19, ANALOGINPUT6); -impl_saadc_input!(P0_20, ANALOGINPUT7); +impl_saadc_input!(P0_13, ANALOG_INPUT0); +impl_saadc_input!(P0_14, ANALOG_INPUT1); +impl_saadc_input!(P0_15, ANALOG_INPUT2); +impl_saadc_input!(P0_16, ANALOG_INPUT3); +impl_saadc_input!(P0_17, ANALOG_INPUT4); +impl_saadc_input!(P0_18, ANALOG_INPUT5); +impl_saadc_input!(P0_19, ANALOG_INPUT6); +impl_saadc_input!(P0_20, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index f8ed11e0..b5a53ed8 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -339,14 +339,14 @@ impl_ppi_channel!(PPI_CH13, 13 => configurable); impl_ppi_channel!(PPI_CH14, 14 => configurable); impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_saadc_input!(P0_13, ANALOGINPUT0); -impl_saadc_input!(P0_14, ANALOGINPUT1); -impl_saadc_input!(P0_15, ANALOGINPUT2); -impl_saadc_input!(P0_16, ANALOGINPUT3); -impl_saadc_input!(P0_17, ANALOGINPUT4); -impl_saadc_input!(P0_18, ANALOGINPUT5); -impl_saadc_input!(P0_19, ANALOGINPUT6); -impl_saadc_input!(P0_20, ANALOGINPUT7); +impl_saadc_input!(P0_13, ANALOG_INPUT0); +impl_saadc_input!(P0_14, ANALOG_INPUT1); +impl_saadc_input!(P0_15, ANALOG_INPUT2); +impl_saadc_input!(P0_16, ANALOG_INPUT3); +impl_saadc_input!(P0_17, ANALOG_INPUT4); +impl_saadc_input!(P0_18, ANALOG_INPUT5); +impl_saadc_input!(P0_19, ANALOG_INPUT6); +impl_saadc_input!(P0_20, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; From 0c9ec8dc362efb3a569b4b85c6fcd97a7f9d7860 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 26 Oct 2022 10:39:29 +0200 Subject: [PATCH 066/374] Update usb --- embassy-nrf/src/usb.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 00da5c9d..eb1472fa 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -313,7 +313,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } }) .await; - regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear. + regs.eventcause.write(|w| w.ready().clear_bit_by_one()); errata::post_enable(); @@ -367,24 +367,24 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { let r = regs.eventcause.read(); if r.isooutcrc().bit() { - regs.eventcause.write(|w| w.isooutcrc().set_bit()); + regs.eventcause.write(|w| w.isooutcrc().detected()); trace!("USB event: isooutcrc"); } if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().set_bit()); + regs.eventcause.write(|w| w.usbwuallowed().allowed()); trace!("USB event: usbwuallowed"); } if r.suspend().bit() { - regs.eventcause.write(|w| w.suspend().set_bit()); + regs.eventcause.write(|w| w.suspend().detected()); regs.lowpower.write(|w| w.lowpower().low_power()); return Poll::Ready(Event::Suspend); } if r.resume().bit() { - regs.eventcause.write(|w| w.resume().set_bit()); + regs.eventcause.write(|w| w.resume().detected()); return Poll::Ready(Event::Resume); } if r.ready().bit() { - regs.eventcause.write(|w| w.ready().set_bit()); + regs.eventcause.write(|w| w.ready().ready()); trace!("USB event: ready"); } @@ -512,7 +512,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } else if r.resume().bit() { Poll::Ready(()) } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().set_bit()); + regs.eventcause.write(|w| w.usbwuallowed().allowed()); regs.dpdmvalue.write(|w| w.state().resume()); regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); From 66611a80caea216241d58cc3b848fe55b8865b49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 11:51:37 +0200 Subject: [PATCH 067/374] Introduce shared new_inner for uart instantiation --- embassy-stm32/src/usart/mod.rs | 63 +++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 07406121..3f5b9952 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -222,10 +222,48 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(_inner, rx, tx, tx_dma, rx_dma); + T::enable(); + T::reset(); + + Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + } + + pub fn new_with_rtscts( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts, rts); T::enable(); T::reset(); + + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + } + + fn new_inner( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(_inner, rx, tx, tx_dma, rx_dma); + let pclk_freq = T::frequency(); // TODO: better calculation, including error checking and OVER8 if possible. @@ -264,29 +302,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } - pub fn new_with_rtscts( - _inner: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - rts: impl Peripheral

> + 'd, - cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(cts, rts); - - unsafe { - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - } - Self::new(_inner, rx, tx, tx_dma, rx_dma, config) - } - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> where TxDma: crate::usart::TxDma, From 80e58426fcf40b6cea28368e43a7289e827461cf Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 10:01:52 +0200 Subject: [PATCH 068/374] Add flash example & flash HIL test --- ci.sh | 2 +- embassy-rp/src/flash.rs | 39 ++++++++++++++--- embassy-rp/src/intrinsics.rs | 1 + embassy-rp/src/lib.rs | 2 + embassy-rp/src/rom_data.rs | 1 + examples/rp/Cargo.toml | 1 + examples/rp/src/bin/flash.rs | 84 ++++++++++++++++++++++++++++++++++++ tests/rp/Cargo.toml | 1 + tests/rp/src/bin/flash.rs | 48 +++++++++++++++++++++ 9 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 examples/rp/src/bin/flash.rs create mode 100644 tests/rp/src/bin/flash.rs diff --git a/ci.sh b/ci.sh index 69440ec3..cd1c0786 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/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 7a26085b..62f68bd3 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,8 +1,13 @@ +use core::marker::PhantomData; + +use embassy_hal_common::Peripheral; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +use crate::peripherals::FLASH; + pub const FLASH_BASE: usize = 0x10000000; // **NOTE**: @@ -46,9 +51,13 @@ impl NorFlashError for Error { } } -pub struct Flash; +pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>); + +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + Self(PhantomData) + } -impl Flash { /// Make sure to uphold the contract points with rp2040-flash. /// - interrupts must be disabled /// - DMA must not access flash memory @@ -81,11 +90,11 @@ impl Flash { } } -impl ErrorType for Flash { +impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { type Error = Error; } -impl ReadNorFlash for Flash { +impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -102,9 +111,9 @@ impl ReadNorFlash for Flash { } } -impl MultiwriteNorFlash for Flash {} +impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} -impl NorFlash for Flash { +impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; @@ -112,6 +121,12 @@ impl NorFlash for Flash { fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { check_erase(self, from, to)?; + trace!( + "Erasing from 0x{:x} to 0x{:x}", + FLASH_BASE as u32 + from, + FLASH_BASE as u32 + to + ); + let len = to - from; unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; @@ -122,7 +137,7 @@ impl NorFlash for Flash { fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { check_write(self, offset, bytes.len())?; - trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset); + trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); let end_offset = offset as usize + bytes.len(); @@ -351,6 +366,7 @@ mod ram_helpers { rom_data::flash_flush_cache(); rom_data::flash_enter_cmd_xip(); */ + #[cfg(target_arch = "arm")] core::arch::asm!( "mov r8, r0", "mov r9, r2", @@ -402,3 +418,12 @@ mod ram_helpers { ); } } + +mod sealed { + pub trait Instance {} +} + +pub trait Instance: sealed::Instance {} + +impl sealed::Instance for FLASH {} +impl Instance for FLASH {} diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 67e8202a..3e75fb7f 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -1,4 +1,5 @@ #![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 diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 44563961..f608f176 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -96,6 +96,8 @@ embassy_hal_common::peripherals! { USB, RTC, + + FLASH, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 8e953dcf..757a2711 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -7,6 +7,7 @@ //! > 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. + // Credit: taken from `rp-hal` (also licensed Apache+MIT) // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 747dde51..38355bbf 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -30,6 +30,7 @@ byte-slice-cast = { version = "1.2.0", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = { version = "0.1.0-alpha.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +embedded-storage = { version = "0.3" } static_cell = "1.0.0" [profile.release] diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs new file mode 100644 index 00000000..17549e4b --- /dev/null +++ b/examples/rp/src/bin/flash.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::peripherals::FLASH; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +const ADDR_OFFSET: u32 = 0x100000; +const FLASH_SIZE: usize = 2 * 1024 * 1024; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); + + erase_write_sector(&mut flash, 0x00); + + multiwrite_bytes(&mut flash, ERASE_SIZE as u32); + + loop {} +} + +fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { + info!(">>>> [multiwrite_bytes]"); + let mut read_buf = [0u8; ERASE_SIZE]; + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=[u8]}", read_buf[0..4]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); + if read_buf.iter().any(|x| *x != 0xFF) { + defmt::panic!("unexpected"); + } + + defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); + defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); + defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); + defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + info!("Contents after write starts with {=[u8]}", read_buf[0..4]); + if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { + defmt::panic!("unexpected"); + } +} + +fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { + info!(">>>> [erase_write_sector]"); + let mut buf = [0u8; ERASE_SIZE]; + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=[u8]}", buf[0..4]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + info!("Contents after erase starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDA; + } + + defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + info!("Contents after write starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xDA) { + defmt::panic!("unexpected"); + } +} diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index d6770d6e..48c88e85 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -22,6 +22,7 @@ embedded-hal-async = { version = "=0.1.0-alpha.2" } 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"] } +embedded-storage = { version = "0.3" } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs new file mode 100644 index 00000000..4cbd78fe --- /dev/null +++ b/tests/rp/src/bin/flash.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +const ADDR_OFFSET: u32 = 0x4000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); + + let mut buf = [0u8; ERASE_SIZE]; + defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + + info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32); + info!("Contents start with {=[u8]}", buf[0..4]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + info!("Contents after erase starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDA; + } + + defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf)); + + defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + info!("Contents after write starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xDA) { + defmt::panic!("unexpected"); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 1669e395654430dff6dffda0cef9522f9ccfd213 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 14:47:32 +0200 Subject: [PATCH 069/374] Buffer data to be written to flash in ram if it does not already reside in ram --- embassy-rp/src/flash.rs | 34 +++++++++++++++++++++++++++------- examples/rp/src/bin/flash.rs | 1 - 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 62f68bd3..0f86684e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -55,6 +55,8 @@ pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + // FIXME: WHY is this needed?! + cortex_m::asm::delay(50_000); Self(PhantomData) } @@ -163,15 +165,24 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S // Write aligned slice of length in multiples of 256 bytes // If the remaining bytes to be written is more than a full page. if remaining_len >= PAGE_SIZE { - let aligned_data = &bytes[start_padding..end_padding]; - - let aligned_offset = if start_padding > 0 { + let mut aligned_offset = if start_padding > 0 { offset as usize + padded_offset } else { offset as usize }; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + if bytes.as_ptr() as usize >= 0x2000_0000 { + let aligned_data = &bytes[start_padding..end_padding]; + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + } else { + for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { + let mut ram_buf = [0xFF_u8; PAGE_SIZE]; + ram_buf.copy_from_slice(chunk); + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) } + aligned_offset += PAGE_SIZE; + } + } } // Pad in the end @@ -273,11 +284,14 @@ mod ram_helpers { pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); flash_function_pointers_with_boot2(true, false, &boot2) } else { flash_function_pointers(true, false) }; + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers); } @@ -300,11 +314,14 @@ mod ram_helpers { pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); flash_function_pointers_with_boot2(true, true, &boot2) } else { flash_function_pointers(true, true) }; + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + write_flash_inner( addr, data.len() as u32, @@ -332,11 +349,14 @@ mod ram_helpers { pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); flash_function_pointers_with_boot2(false, true, &boot2) } else { flash_function_pointers(false, true) }; + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + write_flash_inner( addr, data.len() as u32, diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 17549e4b..d4dfca75 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -18,7 +18,6 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); - erase_write_sector(&mut flash, 0x00); multiwrite_bytes(&mut flash, ERASE_SIZE as u32); From eeb072d9cbc457892c58670ca6fefacf8c80a32b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 16:47:29 +0200 Subject: [PATCH 070/374] Update Rust nightly. --- embassy-embedded-hal/Cargo.toml | 2 +- embassy-lora/Cargo.toml | 2 +- embassy-net/Cargo.toml | 2 +- embassy-net/src/tcp.rs | 18 +++++++++--------- embassy-nrf/Cargo.toml | 4 ++-- embassy-nrf/src/buffered_uarte.rs | 16 ++++++++-------- embassy-rp/Cargo.toml | 4 ++-- embassy-rp/src/uart/buffered.rs | 16 ++++++++-------- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/sdmmc/mod.rs | 12 ++++++------ embassy-stm32/src/usart/buffered.rs | 16 ++++++++-------- embassy-sync/Cargo.toml | 2 +- embassy-sync/src/pipe.rs | 18 +++++++++--------- embassy-time/Cargo.toml | 2 +- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 4 ++-- examples/std/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/rp/Cargo.toml | 4 ++-- tests/stm32/Cargo.toml | 2 +- 26 files changed, 74 insertions(+), 74 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index d0be6d19..85ee856a 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -20,7 +20,7 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true } +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } nb = "1.0.0" diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index ea2c3fe6..dc200417 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -32,7 +32,7 @@ embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 967ef26a..76217075 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -42,7 +42,7 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embedded-io = { version = "0.3.0", optional = true } +embedded-io = { version = "0.3.1", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index f8fff3e2..f3bd2361 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -292,7 +292,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -302,7 +302,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -310,7 +310,7 @@ mod embedded_io_impls { self.io.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -324,7 +324,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpReader<'d> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -338,7 +338,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -346,7 +346,7 @@ mod embedded_io_impls { self.io.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -445,7 +445,7 @@ pub mod client { impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -457,7 +457,7 @@ pub mod client { impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -465,7 +465,7 @@ pub mod client { self.socket.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index dca5fd1b..67b6bec4 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -75,8 +75,8 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} -embedded-io = { version = "0.3.0", features = ["async"], optional = true } +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-io = { version = "0.3.1", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 6e85a159..9c8fe65f 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -341,7 +341,7 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUar } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -351,7 +351,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -361,7 +361,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read f } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -375,7 +375,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -389,7 +389,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRea } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -397,7 +397,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff self.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -407,7 +407,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -415,7 +415,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write self.inner.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index c5685841..d2196be7 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -53,12 +53,12 @@ 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 } +embedded-io = { version = "0.3.1", 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"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 87e16f0e..4f0a5553 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -355,7 +355,7 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -376,7 +376,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -397,7 +397,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -419,7 +419,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -441,7 +441,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -455,7 +455,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { }) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -465,7 +465,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -479,7 +479,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> }) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9194ae78..0b88e89c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,7 +44,7 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-storage = "0.3.0" @@ -67,7 +67,7 @@ nb = "1.0.0" stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" -embedded-io = { version = "0.3.0", features = ["async"], optional = true } +embedded-io = { version = "0.3.1", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a8bc6385..c91f3c8b 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1534,14 +1534,14 @@ mod sdmmc_rs { impl<'d, T: Instance, P: Pins> BlockDevice for Sdmmc<'d, T, P> { type Error = Error; - type ReadFuture<'a> + + type ReadFuture<'a> = impl Future> + 'a where - Self: 'a, - = impl Future> + 'a; - type WriteFuture<'a> + Self: 'a; + + type WriteFuture<'a> = impl Future> + 'a where - Self: 'a, - = impl Future> + 'a; + Self: 'a; fn read<'a>( &'a mut self, diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 2a711bc0..c30bbc20 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -283,7 +283,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { } impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -293,7 +293,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -303,7 +303,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -317,7 +317,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -331,7 +331,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<' } impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -339,7 +339,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { self.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -349,7 +349,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -357,7 +357,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, self.inner.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 584d5ba9..cd6ff07d 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -32,7 +32,7 @@ atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.3.0" +embedded-io = "0.3.1" [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 7d64b648..cd577f34 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -361,7 +361,7 @@ mod io_impls { } impl embedded_io::asynch::Read for Pipe { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -371,7 +371,7 @@ mod io_impls { } impl embedded_io::asynch::Write for Pipe { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -379,7 +379,7 @@ mod io_impls { Pipe::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -393,7 +393,7 @@ mod io_impls { } impl embedded_io::asynch::Read for &Pipe { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -403,7 +403,7 @@ mod io_impls { } impl embedded_io::asynch::Write for &Pipe { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -411,7 +411,7 @@ mod io_impls { Pipe::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -425,7 +425,7 @@ mod io_impls { } impl embedded_io::asynch::Read for Reader<'_, M, N> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -439,7 +439,7 @@ mod io_impls { } impl embedded_io::asynch::Write for Writer<'_, M, N> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -447,7 +447,7 @@ mod io_impls { Writer::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c51a71d0..0fd4645a 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -107,7 +107,7 @@ log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 6949042e..c633f82f 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -17,7 +17,7 @@ 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 } -embedded-io = "0.3.0" +embedded-io = "0.3.1" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 747dde51..ec9896b7 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -28,8 +28,8 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.1" } -embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +embedded-hal-async = { version = "0.1.0-alpha.3" } +embedded-io = { version = "0.3.1", features = ["async", "defmt"] } static_cell = "1.0.0" [profile.release] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index b9bd1e71..79025838 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } -embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } +embedded-io = { version = "0.3.1", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 6d4f09fb..b05457ea 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = "0.3" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-io = "0.3.0" +embedded-io = "0.3.1" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index dad92c0f..b14afd2f 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,7 +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", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 1a05b9ec..0dccff6e 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,7 +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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" @@ -19,7 +19,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } embedded-nal-async = "0.2.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e1120f4..8b00773b 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -22,7 +22,7 @@ defmt = "0.3" defmt-rtt = "0.3" embedded-storage = "0.3.0" -embedded-io = "0.3.0" +embedded-io = "0.3.1" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 657605eb..83d456b2 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -20,7 +20,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 63eac3ed..848723f8 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -26,5 +26,5 @@ embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } static_cell = "1.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1ec19e58..c05aac3f 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-09-22" +channel = "nightly-2022-10-25" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index d6770d6e..e1a6dce4 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -18,10 +18,10 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } 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"] } +embedded-io = { version = "0.3.1", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bebbf557..602c1fb5 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From ac6995f9e656a724d92590e722ac0c25f417893b Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 26 Oct 2022 17:48:22 +0300 Subject: [PATCH 071/374] Fix a bug identified during code review --- embassy-time/src/queue_generic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 83f73484..6769d6a5 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::cmp::Ordering; +use core::cmp::{min, Ordering}; use core::task::Waker; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -74,7 +74,7 @@ impl InnerQueue { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { - timer.at = at; + timer.at = min(timer.at, at); timer.finish(); }) .unwrap_or_else(|| { From 560eecdb737642e6aba132408039195c7d6b6dd8 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 26 Oct 2022 18:05:27 +0300 Subject: [PATCH 072/374] Remove the _embassy_time_schedule_wake magic --- embassy-executor/src/raw/mod.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 5bcb1e6e..181dabe8 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -444,14 +444,21 @@ pub unsafe fn wake_task(task: NonNull) { } #[cfg(feature = "integrated-timers")] -#[no_mangle] -unsafe fn _embassy_time_schedule_wake(at: Instant, waker: &core::task::Waker) { - let task = waker::task_from_waker(waker); - let task = task.as_ref(); - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); +struct TimerQueue; + +#[cfg(feature = "integrated-timers")] +impl embassy_time::queue::TimerQueue for TimerQueue { + fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { + let task = waker::task_from_waker(waker); + let task = unsafe { task.as_ref() }; + let expires_at = task.expires_at.get(); + task.expires_at.set(expires_at.min(at)); + } } +#[cfg(feature = "integrated-timers")] +embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); + #[cfg(feature = "rtos-trace")] impl rtos_trace::RtosTraceOSCallbacks for Executor { fn task_list() { From 9cac649fcf5a633a89aa1b6e550d641270d14956 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Wed, 26 Oct 2022 18:58:22 +0200 Subject: [PATCH 073/374] stm32: Add support for read_until_idle on UART --- embassy-stm32/src/usart/buffered.rs | 34 +- embassy-stm32/src/usart/mod.rs | 402 +++++++++++++++++++-- examples/stm32f3/src/bin/usart_dma.rs | 4 +- examples/stm32f4/src/bin/usart.rs | 4 +- examples/stm32f4/src/bin/usart_buffered.rs | 15 +- examples/stm32f4/src/bin/usart_dma.rs | 4 +- examples/stm32f7/src/bin/usart_dma.rs | 4 +- examples/stm32h7/src/bin/usart.rs | 4 +- examples/stm32h7/src/bin/usart_dma.rs | 4 +- examples/stm32h7/src/bin/usart_split.rs | 4 +- examples/stm32l0/src/bin/usart_dma.rs | 4 +- examples/stm32l0/src/bin/usart_irq.rs | 12 +- examples/stm32l4/src/bin/usart.rs | 4 +- examples/stm32l4/src/bin/usart_dma.rs | 4 +- tests/stm32/src/bin/usart.rs | 17 +- tests/stm32/src/bin/usart_dma.rs | 48 ++- 16 files changed, 500 insertions(+), 68 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c30bbc20..3be0677b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -46,16 +46,44 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( state: &'d mut State<'d, T>, - _uart: Uart<'d, T, NoDma, NoDma>, + _peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], + config: Config, ) -> BufferedUart<'d, T> { - into_ref!(irq); + into_ref!(_peri, rx, tx, irq); + + T::enable(); + T::reset(); let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + unsafe { - r.cr1().modify(|w| { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + r.cr2().write(|_w| {}); + r.cr3().write(|_w| {}); + r.cr1().write(|w| { + w.set_ue(true); + w.set_te(true); + w.set_re(true); + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); w.set_rxneie(true); w.set_idleie(true); }); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 3f5b9952..dee466b2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1,7 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::marker::PhantomData; +use core::task::Poll; +use atomic_polyfill::{compiler_fence, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::NoDma; @@ -10,6 +14,7 @@ use crate::gpio::sealed::AFType; use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; #[cfg(not(any(lpuart_v1, lpuart_v2)))] use crate::pac::usart::{regs, vals, Usart as Regs}; +use crate::time::Hertz; use crate::{peripherals, Peripheral}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -44,6 +49,10 @@ pub struct Config { pub data_bits: DataBits, pub stop_bits: StopBits, pub parity: Parity, + /// if true, on read-like method, if there is a latent error pending, + /// read will abort, the error reported and cleared + /// if false, the error is ignored and cleared + pub detect_previous_overrun: bool, } impl Default for Config { @@ -53,6 +62,8 @@ impl Default for Config { data_bits: DataBits::DataBits8, stop_bits: StopBits::STOP1, parity: Parity::ParityNone, + // historical behavior + detect_previous_overrun: false, } } } @@ -70,10 +81,11 @@ pub enum Error { Overrun, /// Parity check error Parity, + /// Buffer too large for DMA + BufferTooLong, } pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { - phantom: PhantomData<&'d mut T>, tx: UartTx<'d, T, TxDma>, rx: UartRx<'d, T, RxDma>, } @@ -84,8 +96,9 @@ pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { } pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { - phantom: PhantomData<&'d mut T>, + _peri: PeripheralRef<'d, T>, rx_dma: PeripheralRef<'d, RxDma>, + detect_previous_overrun: bool, } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { @@ -135,10 +148,112 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { - fn new(rx_dma: PeripheralRef<'d, RxDma>) -> Self { + /// usefull if you only want Uart Rx. It saves 1 pin and consumes a little less power + pub fn new( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, irq, rx, rx_dma); + + T::enable(); + T::reset(); + + let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + + r.cr2().write(|_w| {}); + r.cr3().write(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + }); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable receiver + w.set_re(true); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + // create state once! + let _s = T::state(); + Self { + _peri: peri, rx_dma, - phantom: PhantomData, + detect_previous_overrun: config.detect_previous_overrun, + } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; + + let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); + + if has_errors { + // clear all interrupts and DMA Rx Request + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); + } else if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + unsafe { + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); + + r.cr3().modify(|w| { + // disable DMA Rx Request + w.set_dmar(false); + }); + } + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); } } @@ -146,17 +261,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { where RxDma: crate::usart::RxDma, { - let ch = &mut self.rx_dma; - let request = ch.request(); - unsafe { - T::regs().cr3().modify(|reg| { - reg.set_dmar(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. - let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); - transfer.await; + self.inner_read(buffer, false).await?; + Ok(()) } @@ -211,13 +317,202 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } Ok(()) } + + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result + where + RxDma: crate::usart::RxDma, + { + self.inner_read(buffer, true).await + } + + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + where + RxDma: crate::usart::RxDma, + { + if buffer.is_empty() { + return Ok(0); + } else if buffer.len() > 0xFFFF { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + let buffer_len = buffer.len(); + + let ch = &mut self.rx_dma; + let request = ch.request(); + + // SAFETY: The only way we might have a problem is using split rx and tx + // here we only modify or read Rx related flags, interrupts and DMA channel + unsafe { + // Start USART DMA + // will not do anything yet because DMAR is not yet set + ch.start_read(request, rdr(r), buffer, Default::default()); + + // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer + if !self.detect_previous_overrun { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + }); + + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); + + compiler_fence(Ordering::SeqCst); + + // In case of errors already pending when reception started, interrupts may have already been raised + // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts + // have been disabled in interrupt handler and DMA Rx Request has been disabled. + + let cr3 = r.cr3().read(); + + if !cr3.dmar() { + // something went wrong + // because the only way to get this flag cleared is to have an interrupt + + // abort DMA transfer + ch.request_stop(); + while ch.is_running() {} + + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + if sr.pe() { + return Err(Error::Parity); + } + if sr.fe() { + return Err(Error::Framing); + } + if sr.ne() { + return Err(Error::Noise); + } + if sr.ore() { + return Err(Error::Overrun); + } + + unreachable!(); + } + + // clear idle flag + if enable_idle_line_detection { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); + } + } + + compiler_fence(Ordering::SeqCst); + + let res = poll_fn(move |cx| { + let s = T::state(); + + ch.set_waker(cx.waker()); + s.rx_waker.register(cx.waker()); + + // SAFETY: read only and we only use Rx related flags + let sr = unsafe { sr(r).read() }; + + // SAFETY: only clears Rx related flags + unsafe { + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + + compiler_fence(Ordering::SeqCst); + + let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); + + if has_errors { + // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler + + // stop dma transfer + ch.request_stop(); + while ch.is_running() {} + + if sr.pe() { + return Poll::Ready(Err(Error::Parity)); + } + if sr.fe() { + return Poll::Ready(Err(Error::Framing)); + } + if sr.ne() { + return Poll::Ready(Err(Error::Noise)); + } + if sr.ore() { + return Poll::Ready(Err(Error::Overrun)); + } + } + + if sr.idle() { + // Idle line + + // stop dma transfer + ch.request_stop(); + while ch.is_running() {} + + let n = buffer_len - (ch.remaining_transfers() as usize); + + return Poll::Ready(Ok(n)); + } else if !ch.is_running() { + // DMA complete + return Poll::Ready(Ok(buffer_len)); + } + + Poll::Pending + }) + .await; + + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + res + } } impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { pub fn new( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, @@ -225,13 +520,14 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable(); T::reset(); - Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } pub fn new_with_rtscts( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, @@ -251,32 +547,29 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_ctse(true); }); } - Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } fn new_inner( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(_inner, rx, tx, tx_dma, rx_dma); - - let pclk_freq = T::frequency(); - - // TODO: better calculation, including error checking and OVER8 if possible. - let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * T::MULTIPLIER; + into_ref!(peri, rx, tx, irq, tx_dma, rx_dma); let r = T::regs(); + configure(r, &config, T::frequency(), T::MULTIPLIER); + unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.brr().write_value(regs::Brr(div)); r.cr1().write(|w| { w.set_ue(true); w.set_te(true); @@ -295,10 +588,20 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } + irq.set_handler(UartRx::::on_interrupt); + irq.unpend(); + irq.enable(); + + // create state once! + let _s = T::state(); + Self { tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), - phantom: PhantomData {}, + rx: UartRx { + _peri: peri, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + }, } } @@ -332,6 +635,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.blocking_read(buffer) } + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result + where + RxDma: crate::usart::RxDma, + { + self.rx.read_until_idle(buffer).await + } + /// Split the Uart into a transmitter and receiver, which is /// particuarly useful when having two tasks correlating to /// transmitting and receiving. @@ -340,6 +650,15 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32) { + // TODO: better calculation, including error checking and OVER8 if possible. + let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; + + unsafe { + r.brr().write_value(regs::Brr(div)); + } +} + mod eh02 { use super::*; @@ -389,6 +708,7 @@ mod eh1 { Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, } } } @@ -573,13 +893,30 @@ unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { } pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + use super::*; + pub struct State { + pub rx_waker: AtomicWaker, + pub tx_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } + } + } + pub trait BasicInstance: crate::rcc::RccPeripheral { const MULTIPLIER: u32; type Interrupt: crate::interrupt::Interrupt; fn regs() -> Regs; + fn state() -> &'static State; } pub trait FullInstance: BasicInstance { @@ -587,7 +924,7 @@ pub(crate) mod sealed { } } -pub trait BasicInstance: sealed::BasicInstance {} +pub trait BasicInstance: Peripheral

+ sealed::BasicInstance + 'static + Send {} pub trait FullInstance: sealed::FullInstance {} @@ -609,6 +946,11 @@ macro_rules! impl_lpuart { fn regs() -> Regs { Regs(crate::pac::$inst.0) } + + fn state() -> &'static crate::usart::sealed::State { + static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); + &STATE + } } impl BasicInstance for peripherals::$inst {} diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 3bc5a287..47121acf 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, NoDma, config); + let irq = interrupt::take!(USART1); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, irq, p.DMA1_CH4, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 90ad882b..8f41bb6c 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -5,6 +5,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); + let irq = interrupt::take!(USART3); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index 7bcecbd2..dd171fe1 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; +use embassy_stm32::usart::{BufferedUart, Config, State}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; @@ -16,13 +15,21 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); let mut state = State::new(); let irq = interrupt::take!(USART3); let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new(&mut state, usart, irq, &mut tx_buf, &mut rx_buf); + let mut buf_usart = BufferedUart::new( + &mut state, + p.USART3, + p.PD9, + p.PD8, + irq, + &mut tx_buf, + &mut rx_buf, + config, + ); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index bb41b8b4..78baeaa0 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, p.DMA1_CH3, NoDma, config); + let irq = interrupt::take!(USART3); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 07270479..4827c52a 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, irq, p.DMA1_CH1, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 87c2b125..405f18ec 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -6,6 +6,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, NoDma, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index 3adffcbe..6e3491e5 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -8,6 +8,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use static_cell::StaticCell; @@ -18,7 +19,8 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index df2b600f..f97176ec 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::peripherals::{DMA1_CH1, UART7}; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -31,7 +32,8 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, p.DMA1_CH1, config); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index 66657d0f..c307f857 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs @@ -4,13 +4,15 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Config::default()); + let irq = interrupt::take!(USART1); + let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH2, p.DMA1_CH3, Config::default()); usart.write(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 0e223738..8e84cd09 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; +use embassy_stm32::usart::{BufferedUart, Config, State}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -21,15 +20,18 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 9600; - let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config); let mut state = State::new(); + let irq = interrupt::take!(USART2); let mut usart = unsafe { BufferedUart::new( &mut state, - usart, - interrupt::take!(USART2), + p.USART2, + p.PA3, + p.PA2, + irq, &mut TX_BUFFER, &mut RX_BUFFER, + config, ) }; diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 4a4b46c5..7d874d9d 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +15,8 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config); + let irq = interrupt::take!(UART4); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 72890689..452bede3 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, p.DMA1_CH3, NoDma, config); + let irq = interrupt::take!(UART4); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 7673bfe6..af55867f 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -7,6 +7,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -18,22 +19,22 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart) = (p.PA9, p.PA10, p.USART1); + let (tx, rx, usart, irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart) = (p.PC4, p.PC5, p.USART1); + let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart) = (p.PC4, p.PC5, p.USART1); + let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart) = (p.PG14, p.PG9, p.USART6); + let (tx, rx, usart, irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart) = (p.PA2, p.PA3, p.LPUART1); + let (tx, rx, usart, irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart) = (p.PB6, p.PB7, p.USART1); + let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart) = (p.PD8, p.PD9, p.USART3); + let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, NoDma, NoDma, config); + let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index e0389446..d12605a9 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -6,6 +6,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -17,22 +18,53 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, p.DMA1_CH4, p.DMA1_CH5); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA9, + p.PA10, + p.USART1, + interrupt::take!(USART1), + p.DMA1_CH4, + p.DMA1_CH5, + ); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, p.DMA2_CH6, p.DMA2_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PG14, + p.PG9, + p.USART6, + interrupt::take!(USART6), + p.DMA2_CH6, + p.DMA2_CH1, + ); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA2, + p.PA3, + p.LPUART1, + interrupt::take!(LPUART1), + p.DMA1_CH1, + p.DMA1_CH2, + ); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, p.GPDMA1_CH0, p.GPDMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PD8, + p.PD9, + p.USART3, + interrupt::take!(USART3), + p.GPDMA1_CH0, + p.GPDMA1_CH1, + ); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, config); + let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. From 4976cbbe6040d5e147e7c42bd29b72d6223b05b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 20:02:58 +0200 Subject: [PATCH 074/374] time/generic-queue: ensure queue goes in .bss instead of .data --- embassy-time/src/queue_generic.rs | 38 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 6769d6a5..8a355b32 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -57,19 +57,11 @@ impl Ord for Timer { struct InnerQueue { queue: SortedLinkedList, - alarm: Option, + alarm: AlarmHandle, alarm_at: Instant, } impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm: None, - alarm_at: Instant::MAX, - } - } - fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) @@ -121,7 +113,7 @@ impl InnerQueue { if self.alarm_at != new_at { self.alarm_at = new_at; - return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks()); + return set_alarm(self.alarm, self.alarm_at.as_ticks()); } } else { self.alarm_at = Instant::MAX; @@ -138,13 +130,13 @@ impl InnerQueue { } struct Queue { - inner: Mutex>, + inner: Mutex>>, } impl Queue { const fn new() -> Self { Self { - inner: Mutex::new(RefCell::new(InnerQueue::new())), + inner: Mutex::new(RefCell::new(None)), } } @@ -152,19 +144,25 @@ impl Queue { self.inner.lock(|inner| { let mut inner = inner.borrow_mut(); - if inner.alarm.is_none() { - let handle = unsafe { allocate_alarm() }.unwrap(); - inner.alarm = Some(handle); + if inner.is_none() {} - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - } - - inner.schedule_wake(at, waker) + inner + .get_or_insert_with(|| { + let handle = unsafe { allocate_alarm() }.unwrap(); + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); + InnerQueue { + queue: SortedLinkedList::new_u8(), + alarm: handle, + alarm_at: Instant::MAX, + } + }) + .schedule_wake(at, waker) }); } fn handle_alarm(&self) { - self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + self.inner + .lock(|inner| inner.borrow_mut().as_mut().unwrap().handle_alarm()); } fn handle_alarm_callback(ctx: *mut ()) { From f9da6271cea7035b2c9f27cfe479aa81889168d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 21:00:50 +0200 Subject: [PATCH 075/374] time/generic_queue: use Vec instead of SortedLinkedList --- embassy-time/src/queue_generic.rs | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 8a355b32..20ae7e6c 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -4,7 +4,7 @@ use core::task::Waker; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; -use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; +use heapless::Vec; use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; use crate::queue::TimerQueue; @@ -56,18 +56,17 @@ impl Ord for Timer { } struct InnerQueue { - queue: SortedLinkedList, + queue: Vec, alarm: AlarmHandle, - alarm_at: Instant, } impl InnerQueue { fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) + .iter_mut() + .find(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { timer.at = min(timer.at, at); - timer.finish(); }) .unwrap_or_else(|| { let mut timer = Timer { @@ -96,35 +95,35 @@ impl InnerQueue { loop { let now = Instant::now(); - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); + let mut next_alarm = Instant::MAX; + + let mut i = 0; + while i < self.queue.len() { + let timer = &self.queue[i]; + if timer.at <= now { + let timer = self.queue.swap_remove(i); + timer.waker.wake(); + } else { + next_alarm = min(next_alarm, timer.at); + i += 1; + } } - if self.update_alarm() { + if self.update_alarm(next_alarm) { break; } } } - fn update_alarm(&mut self) -> bool { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - - return set_alarm(self.alarm, self.alarm_at.as_ticks()); - } + fn update_alarm(&mut self, next_alarm: Instant) -> bool { + if next_alarm == Instant::MAX { + true } else { - self.alarm_at = Instant::MAX; + set_alarm(self.alarm, next_alarm.as_ticks()) } - - true } fn handle_alarm(&mut self) { - self.alarm_at = Instant::MAX; - self.dispatch(); } } @@ -151,9 +150,8 @@ impl Queue { let handle = unsafe { allocate_alarm() }.unwrap(); set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); InnerQueue { - queue: SortedLinkedList::new_u8(), + queue: Vec::new(), alarm: handle, - alarm_at: Instant::MAX, } }) .schedule_wake(at, waker) From d2246ae693d15187f3af9c263bc02809ba096b80 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 22:11:08 +0200 Subject: [PATCH 076/374] Release embassy-sync, embassy-time v0.1.0 --- embassy-sync/Cargo.toml | 3 +++ embassy-time/Cargo.toml | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index cd6ff07d..b7fe1643 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -19,6 +19,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-sync/ features = ["nightly"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["nightly"] + [features] nightly = ["embedded-io/async"] std = [] diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 9ed4d57e..e3d2cbcd 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -11,6 +11,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/ features = ["nightly", "defmt", "unstable-traits", "std"] target = "x86_64-unknown-linux-gnu" +[package.metadata.docs.rs] +features = ["nightly", "defmt", "unstable-traits", "std"] + [features] std = ["tick-hz-1_000_000"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] @@ -126,7 +129,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optiona embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.1.0", path = "../embassy-macros"} embassy-sync = { version = "0.1", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" From 61560e740dea1b4c7ca036dafd66c834a1ff92e2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 22:18:10 +0200 Subject: [PATCH 077/374] time: add missing cargo manifest fields. --- embassy-time/Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index e3d2cbcd..9487003c 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -2,8 +2,16 @@ name = "embassy-time" version = "0.1.0" edition = "2021" +description = "Instant and Duration for embedded no-std systems, with async timer support" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" license = "MIT OR Apache-2.0" - +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/" From a5b1d2237fc327f71a7fb09456ceb448a796efd7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 00:31:46 -0500 Subject: [PATCH 078/374] Refactor: Factor out `SampleTime` --- embassy-stm32/src/adc/f1.rs | 57 +------------- embassy-stm32/src/adc/mod.rs | 5 ++ embassy-stm32/src/adc/sample_time.rs | 114 +++++++++++++++++++++++++++ embassy-stm32/src/adc/v2.rs | 38 +-------- embassy-stm32/src/adc/v3.rs | 112 +------------------------- embassy-stm32/src/adc/v4.rs | 53 +------------ 6 files changed, 123 insertions(+), 256 deletions(-) create mode 100644 embassy-stm32/src/adc/sample_time.rs diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index c5b317ce..56d1a2f3 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; +use crate::adc::{AdcPin, Instance, SampleTime}; use crate::rcc::get_freqs; use crate::time::Hertz; use crate::Peripheral; @@ -29,61 +29,6 @@ impl super::sealed::AdcPin for Temperature { } } -mod sample_time { - /// ADC sample time - /// - /// The default setting is 1.5 ADC clock cycles. - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b001, - - /// 13.5 ADC clock cycles - Cycles13_5 = 0b010, - - /// 28.5 ADC clock cycles - Cycles28_5 = 0b011, - - /// 41.5 ADC clock cycles - Cycles41_5 = 0b100, - - /// 55.5 ADC clock cycles - Cycles55_5 = 0b101, - - /// 71.5 ADC clock cycles - Cycles71_5 = 0b110, - - /// 239.5 ADC clock cycles - Cycles239_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5, - SampleTime::Cycles13_5 => crate::pac::adc::vals::SampleTime::CYCLES13_5, - SampleTime::Cycles28_5 => crate::pac::adc::vals::SampleTime::CYCLES28_5, - SampleTime::Cycles41_5 => crate::pac::adc::vals::SampleTime::CYCLES41_5, - SampleTime::Cycles55_5 => crate::pac::adc::vals::SampleTime::CYCLES55_5, - SampleTime::Cycles71_5 => crate::pac::adc::vals::SampleTime::CYCLES71_5, - SampleTime::Cycles239_5 => crate::pac::adc::vals::SampleTime::CYCLES239_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles28_5 - } - } -} - -pub use sample_time::SampleTime; - pub struct Adc<'d, T: Instance> { sample_time: SampleTime, phantom: PhantomData<&'d mut T>, diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 0eb4eba7..6232678f 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -8,8 +8,13 @@ #[cfg_attr(adc_v1, path = "v1.rs")] mod _version; +#[cfg(not(adc_v1))] +mod sample_time; + #[allow(unused)] pub use _version::*; +#[cfg(not(adc_v1))] +pub use sample_time::SampleTime; use crate::peripherals; diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs new file mode 100644 index 00000000..f0eb104f --- /dev/null +++ b/embassy-stm32/src/adc/sample_time.rs @@ -0,0 +1,114 @@ +macro_rules! impl_sample_time { + ($default_doc:expr, $default:ident, $pac:ty, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { + #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + $( + #[doc = concat!($doc, " ADC clock cycles.")] + $variant, + )* + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> $pac { + match self { + $(Self::$variant => <$pac>::$pac_variant),* + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::$default + } + } + }; +} + +// TODO: Fix default +#[cfg(adc_f1)] +impl_sample_time!( + "1.5", + Cycles28_5, + crate::pac::adc::vals::SampleTime, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("7.5", Cycles7_5, CYCLES7_5), + ("13.5", Cycles13_5, CYCLES13_5), + ("28.5", Cycles28_5, CYCLES28_5), + ("41.5", Cycles41_5, CYCLES41_5), + ("55.5", Cycles55_5, CYCLES55_5), + ("71.5", Cycles71_5, CYCLES71_5), + ("239.5", Cycles239_5, CYCLES239_5) + ) +); + +// TODO: Fix 85 vs 84 +#[cfg(adc_v2)] +impl_sample_time!( + "3", + Cycles3, + crate::pac::adc::vals::Smp, + ( + ("3", Cycles3, CYCLES3), + ("15", Cycles15, CYCLES15), + ("28", Cycles28, CYCLES28), + ("56", Cycles56, CYCLES56), + ("85", Cycles85, CYCLES84), + ("112", Cycles112, CYCLES112), + ("144", Cycles144, CYCLES144), + ("480", Cycles480, CYCLES480) + ) +); + +#[cfg(adc_v3)] +impl_sample_time!( + "2.5", + Cycles2_5, + crate::pac::adc::vals::SampleTime, + ( + ("2.5", Cycles2_5, CYCLES2_5), + ("6.5", Cycles6_5, CYCLES6_5), + ("12.5", Cycles12_5, CYCLES12_5), + ("24.5", Cycles24_5, CYCLES24_5), + ("47.5", Cycles47_5, CYCLES47_5), + ("92.5", Cycles92_5, CYCLES92_5), + ("247.5", Cycles247_5, CYCLES247_5), + ("640.5", Cycles640_5, CYCLES640_5) + ) +); + +#[cfg(adc_g0)] +impl_sample_time!( + "1.5", + Cycles1_5, + crate::pac::adc::vals::SampleTime, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("3.5", Cycles3_5, CYCLES3_5), + ("7.5", Cycles7_5, CYCLES7_5), + ("12.5", Cycles12_5, CYCLES12_5), + ("19.5", Cycles19_5, CYCLES19_5), + ("39.5", Cycles39_5, CYCLES39_5), + ("79.5", Cycles79_5, CYCLES79_5), + ("160.5", Cycles160_5, CYCLES160_5) + ) +); + +// TODO: Fix default doc +#[cfg(adc_v4)] +impl_sample_time!( + "2.5", + Cycles1_5, + crate::pac::adc::vals::Smp, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("2.5", Cycles2_5, CYCLES2_5), + ("8.5", Cycles8_5, CYCLES8_5), + ("16.5", Cycles16_5, CYCLES16_5), + ("32.5", Cycles32_5, CYCLES32_5), + ("64.5", Cycles64_5, CYCLES64_5), + ("387.5", Cycles387_5, CYCLES387_5), + ("810.5", Cycles810_5, CYCLES810_5) + ) +); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 53419c7f..00d9b307 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; -use crate::adc::{AdcPin, Instance}; +use crate::adc::{AdcPin, Instance, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -94,42 +94,6 @@ impl super::sealed::InternalChannel for Vbat { } } -/// ADC sample time -/// -/// The default setting is 3 ADC clock cycles. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum SampleTime { - Cycles3 = 0b000, - Cycles15 = 0b001, - Cycles28 = 0b010, - Cycles56 = 0b011, - Cycles85 = 0b100, - Cycles112 = 0b101, - Cycles144 = 0b110, - Cycles480 = 0b111, -} - -impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { - match self { - SampleTime::Cycles3 => crate::pac::adc::vals::Smp::CYCLES3, - SampleTime::Cycles15 => crate::pac::adc::vals::Smp::CYCLES15, - SampleTime::Cycles28 => crate::pac::adc::vals::Smp::CYCLES28, - SampleTime::Cycles56 => crate::pac::adc::vals::Smp::CYCLES56, - SampleTime::Cycles85 => crate::pac::adc::vals::Smp::CYCLES84, - SampleTime::Cycles112 => crate::pac::adc::vals::Smp::CYCLES112, - SampleTime::Cycles144 => crate::pac::adc::vals::Smp::CYCLES144, - SampleTime::Cycles480 => crate::pac::adc::vals::Smp::CYCLES480, - } - } -} - -impl Default for SampleTime { - fn default() -> Self { - Self::Cycles3 - } -} - enum Prescaler { Div2, Div4, diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 816feeac..b638e8ca 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; +use crate::adc::{AdcPin, Instance, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -93,116 +93,6 @@ impl super::sealed::AdcPin for Vbat { } } -#[cfg(not(adc_g0))] -mod sample_time { - /// ADC sample time - /// - /// The default setting is 2.5 ADC clock cycles. - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 2.5 ADC clock cycles - Cycles2_5 = 0b000, - - /// 6.5 ADC clock cycles - Cycles6_5 = 0b001, - - /// 12.5 ADC clock cycles - Cycles12_5 = 0b010, - - /// 24.5 ADC clock cycles - Cycles24_5 = 0b011, - - /// 47.5 ADC clock cycles - Cycles47_5 = 0b100, - - /// 92.5 ADC clock cycles - Cycles92_5 = 0b101, - - /// 247.5 ADC clock cycles - Cycles247_5 = 0b110, - - /// 640.5 ADC clock cycles - Cycles640_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5, - SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5, - SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, - SampleTime::Cycles24_5 => crate::pac::adc::vals::SampleTime::CYCLES24_5, - SampleTime::Cycles47_5 => crate::pac::adc::vals::SampleTime::CYCLES47_5, - SampleTime::Cycles92_5 => crate::pac::adc::vals::SampleTime::CYCLES92_5, - SampleTime::Cycles247_5 => crate::pac::adc::vals::SampleTime::CYCLES247_5, - SampleTime::Cycles640_5 => crate::pac::adc::vals::SampleTime::CYCLES640_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles2_5 - } - } -} - -#[cfg(adc_g0)] -mod sample_time { - /// ADC sample time - /// - /// The default setting is 1.5 ADC clock cycles. - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 3.5 ADC clock cycles - Cycles3_5 = 0b001, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b010, - - /// 12.5 ADC clock cycles - Cycles12_5 = 0b011, - - /// 19.5 ADC clock cycles - Cycles19_5 = 0b100, - - /// 39.5 ADC clock cycles - Cycles39_5 = 0b101, - - /// 79.5 ADC clock cycles - Cycles79_5 = 0b110, - - /// 160.5 ADC clock cycles - Cycles160_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5, - SampleTime::Cycles3_5 => crate::pac::adc::vals::SampleTime::CYCLES3_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5, - SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, - SampleTime::Cycles19_5 => crate::pac::adc::vals::SampleTime::CYCLES19_5, - SampleTime::Cycles39_5 => crate::pac::adc::vals::SampleTime::CYCLES39_5, - SampleTime::Cycles79_5 => crate::pac::adc::vals::SampleTime::CYCLES79_5, - SampleTime::Cycles160_5 => crate::pac::adc::vals::SampleTime::CYCLES160_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } - } -} - -pub use sample_time::SampleTime; - pub struct Adc<'d, T: Instance> { sample_time: SampleTime, resolution: Resolution, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 2b8f1053..b9222947 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance, InternalChannel}; +use super::{AdcPin, Instance, InternalChannel, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -193,57 +193,6 @@ foreach_peripheral!( }; ); -/// ADC sample time -/// -/// The default setting is 2.5 ADC clock cycles. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5, - - /// 2.5 ADC clock cycles - Cycles2_5, - - /// 8.5 ADC clock cycles - Cycles8_5, - - /// 16.5 ADC clock cycles - Cycles16_5, - - /// 32.5 ADC clock cycles - Cycles32_5, - - /// 64.5 ADC clock cycles - Cycles64_5, - - /// 387.5 ADC clock cycles - Cycles387_5, - - /// 810.5 ADC clock cycles - Cycles810_5, -} - -impl SampleTime { - pub(crate) fn sample_time(&self) -> pac::adc::vals::Smp { - match self { - SampleTime::Cycles1_5 => pac::adc::vals::Smp::CYCLES1_5, - SampleTime::Cycles2_5 => pac::adc::vals::Smp::CYCLES2_5, - SampleTime::Cycles8_5 => pac::adc::vals::Smp::CYCLES8_5, - SampleTime::Cycles16_5 => pac::adc::vals::Smp::CYCLES16_5, - SampleTime::Cycles32_5 => pac::adc::vals::Smp::CYCLES32_5, - SampleTime::Cycles64_5 => pac::adc::vals::Smp::CYCLES64_5, - SampleTime::Cycles387_5 => pac::adc::vals::Smp::CYCLES387_5, - SampleTime::Cycles810_5 => pac::adc::vals::Smp::CYCLES810_5, - } - } -} - -impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } -} - // NOTE (unused): The prescaler enum closely copies the hardware capabilities, // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. #[allow(unused)] From 51426747863f5d73f5d8c55d39add1b1aeb3356a Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 01:44:48 -0500 Subject: [PATCH 079/374] Fix pre-existing `SampleTime` typos --- embassy-stm32/src/adc/sample_time.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index f0eb104f..1850d80f 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -25,11 +25,10 @@ macro_rules! impl_sample_time { }; } -// TODO: Fix default #[cfg(adc_f1)] impl_sample_time!( "1.5", - Cycles28_5, + Cycles1_5, crate::pac::adc::vals::SampleTime, ( ("1.5", Cycles1_5, CYCLES1_5), @@ -43,7 +42,6 @@ impl_sample_time!( ) ); -// TODO: Fix 85 vs 84 #[cfg(adc_v2)] impl_sample_time!( "3", @@ -54,7 +52,7 @@ impl_sample_time!( ("15", Cycles15, CYCLES15), ("28", Cycles28, CYCLES28), ("56", Cycles56, CYCLES56), - ("85", Cycles85, CYCLES84), + ("84", Cycles84, CYCLES84), ("112", Cycles112, CYCLES112), ("144", Cycles144, CYCLES144), ("480", Cycles480, CYCLES480) @@ -95,10 +93,9 @@ impl_sample_time!( ) ); -// TODO: Fix default doc #[cfg(adc_v4)] impl_sample_time!( - "2.5", + "1.5", Cycles1_5, crate::pac::adc::vals::Smp, ( From 7b38b95e1036d2a62e1d0abfc41b064839497242 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 02:04:52 -0500 Subject: [PATCH 080/374] Refactor: Factor out `Resolution` --- embassy-stm32/src/adc/mod.rs | 5 +++ embassy-stm32/src/adc/resolution.rs | 59 +++++++++++++++++++++++++++++ embassy-stm32/src/adc/v2.rs | 35 +---------------- embassy-stm32/src/adc/v3.rs | 35 +---------------- embassy-stm32/src/adc/v4.rs | 38 +------------------ 5 files changed, 67 insertions(+), 105 deletions(-) create mode 100644 embassy-stm32/src/adc/resolution.rs diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 6232678f..35d84f97 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -11,8 +11,13 @@ mod _version; #[cfg(not(adc_v1))] mod sample_time; +#[cfg(not(any(adc_f1, adc_v1)))] +mod resolution; + #[allow(unused)] pub use _version::*; +#[cfg(not(any(adc_f1, adc_v1)))] +pub use resolution::Resolution; #[cfg(not(adc_v1))] pub use sample_time::SampleTime; diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs new file mode 100644 index 00000000..354cabfb --- /dev/null +++ b/embassy-stm32/src/adc/resolution.rs @@ -0,0 +1,59 @@ +#[cfg(any(adc_v2, adc_v3, adc_g0))] +pub enum Resolution { + TwelveBit, + TenBit, + EightBit, + SixBit, +} + +#[cfg(adc_v4)] +pub enum Resolution { + SixteenBit, + FourteenBit, + TwelveBit, + TenBit, + EightBit, +} + +impl Default for Resolution { + fn default() -> Self { + #[cfg(any(adc_v2, adc_v3, adc_g0))] + { + Self::TwelveBit + } + #[cfg(adc_v4)] + { + Self::SixteenBit + } + } +} + +impl Resolution { + pub(super) fn res(&self) -> crate::pac::adc::vals::Res { + match self { + #[cfg(adc_v4)] + Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT, + #[cfg(adc_v4)] + Resolution::FourteenBit => crate::pac::adc::vals::Res::FOURTEENBITV, + Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, + Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, + Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, + #[cfg(any(adc_v2, adc_v3, adc_g0))] + Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, + } + } + + pub fn to_max_count(&self) -> u32 { + match self { + #[cfg(adc_v4)] + Resolution::SixteenBit => (1 << 16) - 1, + #[cfg(adc_v4)] + Resolution::FourteenBit => (1 << 14) - 1, + Resolution::TwelveBit => (1 << 12) - 1, + Resolution::TenBit => (1 << 10) - 1, + Resolution::EightBit => (1 << 8) - 1, + #[cfg(any(adc_v2, adc_v3, adc_g0))] + Resolution::SixBit => (1 << 6) - 1, + } + } +} diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 00d9b307..e2678fe4 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; -use crate::adc::{AdcPin, Instance, SampleTime}; +use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -17,39 +17,6 @@ pub const VREF_CALIB_MV: u32 = 3300; /// ADC turn-on time pub const ADC_POWERUP_TIME_US: u32 = 3; -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> crate::pac::adc::vals::Res { - match self { - Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, - Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub struct VrefInt; impl InternalChannel for VrefInt {} impl super::sealed::InternalChannel for VrefInt { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index b638e8ca..7bc130e5 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance, SampleTime}; +use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -24,39 +24,6 @@ fn enable() { }); } -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> crate::pac::adc::vals::Res { - match self { - Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, - Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub struct VrefInt; impl AdcPin for VrefInt {} impl super::sealed::AdcPin for VrefInt { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b9222947..9dc5318e 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance, InternalChannel, SampleTime}; +use super::{AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -14,42 +14,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -pub enum Resolution { - SixteenBit, - FourteenBit, - TwelveBit, - TenBit, - EightBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::SixteenBit - } -} - -impl Resolution { - fn res(&self) -> pac::adc::vals::Res { - match self { - Resolution::SixteenBit => pac::adc::vals::Res::SIXTEENBIT, - Resolution::FourteenBit => pac::adc::vals::Res::FOURTEENBITV, - Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBITV, - Resolution::TenBit => pac::adc::vals::Res::TENBIT, - Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::SixteenBit => (1 << 16) - 1, - Resolution::FourteenBit => (1 << 14) - 1, - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - } - } -} - // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} From 2cfe2439c980129b2dc8170f35944f3c0505c230 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 02:28:39 -0500 Subject: [PATCH 081/374] Refactor: Impl `From` for `SampleTime` and `Resolution` --- embassy-stm32/src/adc/f1.rs | 9 +++------ embassy-stm32/src/adc/mod.rs | 5 ++--- embassy-stm32/src/adc/resolution.rs | 10 +++++++--- embassy-stm32/src/adc/sample_time.rs | 8 ++++---- embassy-stm32/src/adc/v2.rs | 11 ++++------- embassy-stm32/src/adc/v3.rs | 15 ++++++--------- embassy-stm32/src/adc/v4.rs | 11 ++++------- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 56d1a2f3..8a3b4be5 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -146,14 +146,11 @@ impl<'d, T: Instance> Adc<'d, T> { } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr2() - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr1() - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 35d84f97..328470ef 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -8,11 +8,10 @@ #[cfg_attr(adc_v1, path = "v1.rs")] mod _version; -#[cfg(not(adc_v1))] -mod sample_time; - #[cfg(not(any(adc_f1, adc_v1)))] mod resolution; +#[cfg(not(adc_v1))] +mod sample_time; #[allow(unused)] pub use _version::*; diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 354cabfb..62b52a46 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,5 @@ #[cfg(any(adc_v2, adc_v3, adc_g0))] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, TenBit, @@ -7,6 +8,7 @@ pub enum Resolution { } #[cfg(adc_v4)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { SixteenBit, FourteenBit, @@ -28,9 +30,9 @@ impl Default for Resolution { } } -impl Resolution { - pub(super) fn res(&self) -> crate::pac::adc::vals::Res { - match self { +impl From for crate::pac::adc::vals::Res { + fn from(res: Resolution) -> crate::pac::adc::vals::Res { + match res { #[cfg(adc_v4)] Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT, #[cfg(adc_v4)] @@ -42,7 +44,9 @@ impl Resolution { Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } +} +impl Resolution { pub fn to_max_count(&self) -> u32 { match self { #[cfg(adc_v4)] diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 1850d80f..60ba8004 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -9,10 +9,10 @@ macro_rules! impl_sample_time { )* } - impl SampleTime { - pub(crate) fn sample_time(&self) -> $pac { - match self { - $(Self::$variant => <$pac>::$pac_variant),* + impl From for $pac { + fn from(sample_time: SampleTime) -> $pac { + match sample_time { + $(SampleTime::$variant => <$pac>::$pac_variant),* } } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e2678fe4..30ac5d87 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -214,7 +214,7 @@ where unsafe fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cr1().modify(|reg| reg.set_res(self.resolution.into())); // Select channel T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); @@ -228,14 +228,11 @@ where } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr2() - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr1() - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 7bc130e5..7962f241 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -199,9 +199,9 @@ impl<'d, T: Instance> Adc<'d, T> { // Configure ADC #[cfg(not(stm32g0))] - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); #[cfg(stm32g0)] - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.into())); // Configure channel Self::set_channel_sample_time(pin.channel(), self.sample_time); @@ -231,19 +231,16 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(stm32g0)] unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { - T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.sample_time())); + T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } #[cfg(not(stm32g0))] unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr1() - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr2() - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 9dc5318e..dc5f62d3 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -409,7 +409,7 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { unsafe fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); // Configure channel Self::set_channel_sample_time(channel, self.sample_time); @@ -427,14 +427,11 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr(0) - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr(1) - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } From 88bbc238b7da95162e4959a62d15c79bfef05149 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 16:45:12 -0500 Subject: [PATCH 082/374] Set resolution directly --- embassy-stm32/src/adc/v2.rs | 7 +++---- embassy-stm32/src/adc/v3.rs | 15 ++++++--------- embassy-stm32/src/adc/v4.rs | 9 +++------ 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 30ac5d87..2cdaa00d 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -94,7 +94,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -120,7 +119,6 @@ where Self { sample_time: Default::default(), - resolution: Resolution::default(), phantom: PhantomData, } } @@ -130,7 +128,9 @@ where } pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; + unsafe { + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + } } /// Enables internal voltage reference and returns [VrefInt], which can be used in @@ -214,7 +214,6 @@ where unsafe fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.into())); // Select channel T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 7962f241..c6898f54 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -62,7 +62,6 @@ impl super::sealed::AdcPin for Vbat { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -99,7 +98,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), - resolution: Resolution::default(), phantom: PhantomData, } } @@ -145,7 +143,12 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; + unsafe { + #[cfg(not(stm32g0))] + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + #[cfg(stm32g0)] + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } } /* @@ -197,12 +200,6 @@ impl<'d, T: Instance> Adc<'d, T> { // spin } - // Configure ADC - #[cfg(not(stm32g0))] - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); - #[cfg(stm32g0)] - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.into())); - // Configure channel Self::set_channel_sample_time(pin.channel(), self.sample_time); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index dc5f62d3..750f3480 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -227,7 +227,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -264,7 +263,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { let mut s = Self { sample_time: Default::default(), - resolution: Resolution::default(), phantom: PhantomData, }; s.power_up(delay); @@ -367,7 +365,9 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; + unsafe { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } } /// Perform a single conversion. @@ -408,9 +408,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } unsafe fn read_channel(&mut self, channel: u8) -> u16 { - // Configure ADC - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); - // Configure channel Self::set_channel_sample_time(channel, self.sample_time); From 6bf24b4d1ab1df57e410de6d6c8f413a2c44d5af Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:21:38 -0500 Subject: [PATCH 083/374] Refactor: Remove unused `Common` trait --- embassy-stm32/src/adc/mod.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 328470ef..08989649 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -29,11 +29,6 @@ pub(crate) mod sealed { fn common_regs() -> &'static crate::pac::adccommon::AdcCommon; } - #[cfg(all(not(adc_f1), not(adc_v1)))] - pub trait Common { - fn regs() -> &'static crate::pac::adccommon::AdcCommon; - } - pub trait AdcPin { fn channel(&self) -> u8; } @@ -47,8 +42,7 @@ pub(crate) mod sealed { pub trait Instance: sealed::Instance + 'static {} #[cfg(any(adc_f1, adc_v2))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} -#[cfg(all(not(adc_f1), not(adc_v1)))] -pub trait Common: sealed::Common + 'static {} + pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} @@ -111,19 +105,6 @@ foreach_peripheral!( }; ); -#[cfg(all(not(adc_f1), not(adc_v1)))] -foreach_peripheral!( - (adccommon, $inst:ident) => { - impl sealed::Common for peripherals::$inst { - fn regs() -> &'static crate::pac::adccommon::AdcCommon { - &crate::pac::$inst - } - } - - impl crate::adc::Common for peripherals::$inst {} - }; -); - macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::adc::AdcPin for crate::peripherals::$pin {} From f363f6ce92879a9cb21f058fa308b05256e7c482 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:33:32 -0500 Subject: [PATCH 084/374] Refactor: Don't return references to pointers --- embassy-stm32/src/adc/mod.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 08989649..439fe0ba 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -24,9 +24,9 @@ use crate::peripherals; pub(crate) mod sealed { pub trait Instance { - fn regs() -> &'static crate::pac::adc::Adc; + fn regs() -> crate::pac::adc::Adc; #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon; + fn common_regs() -> crate::pac::adccommon::AdcCommon; } pub trait AdcPin { @@ -50,14 +50,14 @@ pub trait InternalChannel: sealed::InternalChannel {} foreach_peripheral!( (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { - fn regs() -> &'static crate::pac::adc::Adc { - &crate::pac::$inst + fn regs() -> crate::pac::adc::Adc { + crate::pac::$inst } #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { - return &crate::pac::$common_inst + return crate::pac::$common_inst }; } } @@ -71,14 +71,14 @@ foreach_peripheral!( foreach_peripheral!( (adc, ADC3) => { impl crate::adc::sealed::Instance for peripherals::ADC3 { - fn regs() -> &'static crate::pac::adc::Adc { - &crate::pac::ADC3 + fn regs() -> crate::pac::adc::Adc { + crate::pac::ADC3 } #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, ADC3_COMMON) => { - return &crate::pac::ADC3_COMMON + return crate::pac::ADC3_COMMON }; } } @@ -88,14 +88,14 @@ foreach_peripheral!( }; (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { - fn regs() -> &'static crate::pac::adc::Adc { - &crate::pac::$inst + fn regs() -> crate::pac::adc::Adc { + crate::pac::$inst } #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, ADC_COMMON) => { - return &crate::pac::ADC_COMMON + return crate::pac::ADC_COMMON }; } } From 9c30d565b970330dd0041e790516ab4517dd3b22 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:51:12 -0500 Subject: [PATCH 085/374] Refactor: Factor out `Adc` struct declaration --- embassy-stm32/src/adc/f1.rs | 7 +------ embassy-stm32/src/adc/mod.rs | 6 ++++++ embassy-stm32/src/adc/v2.rs | 7 +------ embassy-stm32/src/adc/v3.rs | 7 +------ embassy-stm32/src/adc/v4.rs | 7 +------ 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 8a3b4be5..4282a655 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, SampleTime}; use crate::rcc::get_freqs; use crate::time::Hertz; use crate::Peripheral; @@ -29,11 +29,6 @@ impl super::sealed::AdcPin for Temperature { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(_peri); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 439fe0ba..70c1025e 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -22,6 +22,12 @@ pub use sample_time::SampleTime; use crate::peripherals; +#[cfg(not(adc_v1))] +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + phantom: core::marker::PhantomData<&'d mut T>, +} + pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 2cdaa00d..f1e65dc5 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; -use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -92,11 +92,6 @@ impl Prescaler { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T> Adc<'d, T> where T: Instance, diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index c6898f54..ee5f278b 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -60,11 +60,6 @@ impl super::sealed::AdcPin for Vbat { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(_peri); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 750f3480..c83674e6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -225,11 +225,6 @@ impl Prescaler { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { embassy_hal_common::into_ref!(_peri); From 4f2dcca34b6e17b07df58ee4f26f852a992e7f8b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:59:44 -0500 Subject: [PATCH 086/374] Refactor: Fix v4 `RccPeripheral` bounds --- embassy-stm32/src/adc/mod.rs | 4 ++-- embassy-stm32/src/adc/v4.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 70c1025e..5d90b8b3 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -44,9 +44,9 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v2)))] +#[cfg(not(any(adc_f1, adc_v2, adc_v4)))] pub trait Instance: sealed::Instance + 'static {} -#[cfg(any(adc_f1, adc_v2))] +#[cfg(any(adc_f1, adc_v2, adc_v4))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} pub trait AdcPin: sealed::AdcPin {} diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c83674e6..12f86885 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -225,7 +225,7 @@ impl Prescaler { } } -impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { embassy_hal_common::into_ref!(_peri); T::enable(); From 08c8022583a12dcbea8905249f3c19630462092e Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 18:04:52 -0500 Subject: [PATCH 087/374] Refactor: Reorder `_version` cfgs --- embassy-stm32/src/adc/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 5d90b8b3..6d8afea5 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,11 +1,10 @@ #![macro_use] -#[cfg_attr(adc_v4, path = "v4.rs")] -#[cfg_attr(adc_v3, path = "v3.rs")] -#[cfg_attr(adc_v2, path = "v2.rs")] -#[cfg_attr(adc_g0, path = "v3.rs")] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] +#[cfg_attr(adc_v2, path = "v2.rs")] +#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] +#[cfg_attr(adc_v4, path = "v4.rs")] mod _version; #[cfg(not(any(adc_f1, adc_v1)))] From 171b764d8275b8da3af5650edfac0d5c4c3e3fe1 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 18:36:04 -0500 Subject: [PATCH 088/374] Refactor: Use `PeripheralRef` --- embassy-stm32/src/adc/f1.rs | 8 +++----- embassy-stm32/src/adc/mod.rs | 7 ++++--- embassy-stm32/src/adc/v2.rs | 8 +++----- embassy-stm32/src/adc/v3.rs | 8 +++----- embassy-stm32/src/adc/v4.rs | 8 +++----- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 4282a655..d30ec001 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; @@ -30,8 +28,8 @@ impl super::sealed::AdcPin for Temperature { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); T::enable(); T::reset(); unsafe { @@ -60,8 +58,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us((1_000_000) / Self::freq().0 + 1); Self { + adc, sample_time: Default::default(), - phantom: PhantomData, } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 6d8afea5..ec49dace 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -23,8 +23,9 @@ use crate::peripherals; #[cfg(not(adc_v1))] pub struct Adc<'d, T: Instance> { + #[allow(unused)] + adc: crate::PeripheralRef<'d, T>, sample_time: SampleTime, - phantom: core::marker::PhantomData<&'d mut T>, } pub(crate) mod sealed { @@ -44,9 +45,9 @@ pub(crate) mod sealed { } #[cfg(not(any(adc_f1, adc_v2, adc_v4)))] -pub trait Instance: sealed::Instance + 'static {} +pub trait Instance: sealed::Instance + crate::Peripheral

{} #[cfg(any(adc_f1, adc_v2, adc_v4))] -pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} +pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index f1e65dc5..11a51f99 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; @@ -96,8 +94,8 @@ impl<'d, T> Adc<'d, T> where T: Instance, { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); T::enable(); T::reset(); @@ -113,8 +111,8 @@ where delay.delay_us(ADC_POWERUP_TIME_US); Self { + adc, sample_time: Default::default(), - phantom: PhantomData, } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index ee5f278b..8f81cb7a 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; @@ -61,8 +59,8 @@ impl super::sealed::AdcPin for Vbat { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); enable(); unsafe { T::regs().cr().modify(|reg| { @@ -92,8 +90,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us(1); Self { + adc, sample_time: Default::default(), - phantom: PhantomData, } } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 12f86885..f5aa0e63 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use atomic_polyfill::{AtomicU8, Ordering}; use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; @@ -226,8 +224,8 @@ impl Prescaler { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - embassy_hal_common::into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + embassy_hal_common::into_ref!(adc); T::enable(); T::reset(); @@ -257,8 +255,8 @@ impl<'d, T: Instance> Adc<'d, T> { } let mut s = Self { + adc, sample_time: Default::default(), - phantom: PhantomData, }; s.power_up(delay); s.configure_differential_inputs(); From 3c6c382465131c6f76567f976198b77e327df4b2 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 27 Oct 2022 07:10:27 +0200 Subject: [PATCH 089/374] Remove random delay from example, and move flash functions to allow using without embedded-storage in scope --- embassy-rp/src/flash.rs | 120 +++++++++++++++++++---------------- examples/rp/src/bin/flash.rs | 8 ++- 2 files changed, 74 insertions(+), 54 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0f86684e..d09cc62f 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -55,51 +55,10 @@ pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { pub fn new(_flash: impl Peripheral

+ 'd) -> Self { - // FIXME: WHY is this needed?! - cortex_m::asm::delay(50_000); Self(PhantomData) } - /// Make sure to uphold the contract points with rp2040-flash. - /// - interrupts must be disabled - /// - DMA must not access flash memory - unsafe fn in_ram(&mut self, operation: impl FnOnce()) { - let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; - - // TODO: Make sure CORE1 is paused during the entire duration of the RAM function - - critical_section::with(|_| { - // Pause all DMA channels for the duration of the ram operation - for (number, status) in dma_status.iter_mut().enumerate() { - let ch = crate::pac::DMA.ch(number as _); - *status = ch.ctrl_trig().read().en(); - if *status { - ch.ctrl_trig().modify(|w| w.set_en(false)); - } - } - - // Run our flash operation in RAM - operation(); - - // Re-enable previously enabled DMA channels - for (number, status) in dma_status.iter().enumerate() { - let ch = crate::pac::DMA.ch(number as _); - if *status { - ch.ctrl_trig().modify(|w| w.set_en(true)); - } - } - }); - } -} - -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { - type Error = Error; -} - -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { check_read(self, offset, bytes.len())?; let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) }; @@ -108,19 +67,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLA Ok(()) } - fn capacity(&self) -> usize { + pub fn capacity(&self) -> usize { FLASH_SIZE } -} -impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} - -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { - const WRITE_SIZE: usize = WRITE_SIZE; - - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { check_erase(self, from, to)?; trace!( @@ -136,7 +87,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S Ok(()) } - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { check_write(self, offset, bytes.len())?; trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); @@ -199,6 +150,69 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S Ok(()) } + + /// Make sure to uphold the contract points with rp2040-flash. + /// - interrupts must be disabled + /// - DMA must not access flash memory + unsafe fn in_ram(&mut self, operation: impl FnOnce()) { + let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; + + // TODO: Make sure CORE1 is paused during the entire duration of the RAM function + + critical_section::with(|_| { + // Pause all DMA channels for the duration of the ram operation + for (number, status) in dma_status.iter_mut().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + *status = ch.ctrl_trig().read().en(); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(false)); + } + } + + // Run our flash operation in RAM + operation(); + + // Re-enable previously enabled DMA channels + for (number, status) in dma_status.iter().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(true)); + } + } + }); + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { + type Error = Error; +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} + +impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } } #[allow(dead_code)] diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index d4dfca75..8d6b379f 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; use embassy_rp::peripherals::FLASH; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDR_OFFSET: u32 = 0x100000; @@ -17,6 +17,12 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); + // add some delay to give an attached debug probe time to parse the + // defmt RTT header. Reading that header might touch flash memory, which + // interferes with flash write operations. + // https://github.com/knurling-rs/defmt/pull/683 + Timer::after(Duration::from_millis(10)).await; + let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); erase_write_sector(&mut flash, 0x00); From a7b90c7fb60e3814e929ee963fd328db2a138ca7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 27 Oct 2022 11:36:46 +0200 Subject: [PATCH 090/374] Remove unused imports from test --- tests/rp/src/bin/flash.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 4cbd78fe..51371963 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; const ADDR_OFFSET: u32 = 0x4000; From bc21b6efafe607e6ed582b048baedb7803483ee7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 27 Oct 2022 12:41:06 +0200 Subject: [PATCH 091/374] Add delay to flash test to allow time to parse RTT header --- tests/rp/src/bin/flash.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 51371963..897e3804 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDR_OFFSET: u32 = 0x4000; @@ -14,6 +15,12 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); + // add some delay to give an attached debug probe time to parse the + // defmt RTT header. Reading that header might touch flash memory, which + // interferes with flash write operations. + // https://github.com/knurling-rs/defmt/pull/683 + Timer::after(Duration::from_millis(10)).await; + let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); let mut buf = [0u8; ERASE_SIZE]; From 9423987ac52b0e4f7a176e70114016acd3752592 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Thu, 27 Oct 2022 10:52:21 +0200 Subject: [PATCH 092/374] embassy-stm32: Add hardware flow control constructor for UartRx and UartTx --- embassy-stm32/src/usart/mod.rs | 117 +++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dee466b2..dde0799b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -102,7 +102,80 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { - fn new(tx_dma: PeripheralRef<'d, TxDma>) -> Self { + /// usefull if you only want Uart Tx. It saves 1 pin and consumes a little less power + pub fn new( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + T::enable(); + T::reset(); + + Self::new_inner(peri, tx, tx_dma, config) + } + + pub fn new_with_cts( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts); + + T::enable(); + T::reset(); + + unsafe { + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_ctse(true); + }); + } + Self::new_inner(peri, tx, tx_dma, config) + } + + fn new_inner( + _peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(_peri, tx, tx_dma); + + let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + + unsafe { + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + r.cr2().write(|_w| {}); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable transceiver + w.set_te(true); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + // create state once! + let _s = T::state(); + Self { tx_dma, phantom: PhantomData, @@ -156,11 +229,44 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(peri, irq, rx, rx_dma); + T::enable(); + T::reset(); + + Self::new_inner(peri, irq, rx, rx_dma, config) + } + + pub fn new_with_rts( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(rts); T::enable(); T::reset(); + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_rtse(true); + }); + } + + Self::new_inner(peri, irq, rx, rx_dma, config) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, irq, rx, rx_dma); + let r = T::regs(); configure(r, &config, T::frequency(), T::MULTIPLIER); @@ -169,7 +275,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx.set_as_af(rx.af_num(), AFType::Input); r.cr2().write(|_w| {}); - r.cr3().write(|w| { + r.cr3().modify(|w| { // enable Error Interrupt: (Frame error, Noise error, Overrun error) w.set_eie(true); }); @@ -596,7 +702,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let _s = T::state(); Self { - tx: UartTx::new(tx_dma), + tx: UartTx { + tx_dma, + phantom: PhantomData, + }, rx: UartRx { _peri: peri, rx_dma, From f053bf742c2e6e5e5e18e36bb773bc1e84010a2a Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Thu, 27 Oct 2022 11:03:37 +0200 Subject: [PATCH 093/374] embassy-stm32: Add support for hardware flow control for BufferedUart --- embassy-stm32/src/usart/buffered.rs | 49 ++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 3be0677b..59c7a8cc 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -45,6 +45,51 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( + state: &'d mut State<'d, T>, + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> BufferedUart<'d, T> { + T::enable(); + T::reset(); + + Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + } + + pub fn new_with_rtscts( + state: &'d mut State<'d, T>, + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> BufferedUart<'d, T> { + into_ref!(cts, rts); + + T::enable(); + T::reset(); + + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + + Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + } + + fn new_inner( state: &'d mut State<'d, T>, _peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -56,9 +101,6 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { ) -> BufferedUart<'d, T> { into_ref!(_peri, rx, tx, irq); - T::enable(); - T::reset(); - let r = T::regs(); configure(r, &config, T::frequency(), T::MULTIPLIER); @@ -68,7 +110,6 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.cr3().write(|_w| {}); r.cr1().write(|w| { w.set_ue(true); w.set_te(true); From 79b49c6fae7b4f7df2c03ae0be970d154f4faac8 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Fri, 28 Oct 2022 09:32:05 +0200 Subject: [PATCH 094/374] embassy-stm32: remove duplicated code for USART general configuration --- embassy-stm32/src/usart/buffered.rs | 23 ++---- embassy-stm32/src/usart/mod.rs | 104 +++++++++------------------- 2 files changed, 38 insertions(+), 89 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 59c7a8cc..0a6d6e14 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -103,28 +103,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } - r.cr2().write(|_w| {}); - r.cr1().write(|w| { - w.set_ue(true); - w.set_te(true); - w.set_re(true); - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); + configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + + unsafe { + r.cr1().modify(|w| { w.set_rxneie(true); w.set_idleie(true); }); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dde0799b..716247d9 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -146,33 +146,12 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - r.cr2().write(|_w| {}); - r.cr1().write(|w| { - // enable uart - w.set_ue(true); - // enable transceiver - w.set_te(true); - // configure word size - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - // configure parity - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); } + configure(r, &config, T::frequency(), T::MULTIPLIER, false, true); + // create state once! let _s = T::state(); @@ -269,37 +248,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { rx.set_as_af(rx.af_num(), AFType::Input); - - r.cr2().write(|_w| {}); - r.cr3().modify(|w| { - // enable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(true); - }); - r.cr1().write(|w| { - // enable uart - w.set_ue(true); - // enable receiver - w.set_re(true); - // configure word size - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - // configure parity - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); } + configure(r, &config, T::frequency(), T::MULTIPLIER, true, false); + irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); @@ -669,31 +623,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - r.cr2().write(|_w| {}); - r.cr1().write(|w| { - w.set_ue(true); - w.set_te(true); - w.set_re(true); - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); } + configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + irq.set_handler(UartRx::::on_interrupt); irq.unpend(); irq.enable(); @@ -759,12 +695,38 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } -fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32) { +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable_rx: bool, enable_tx: bool) { + if !enable_rx && !enable_tx { + panic!("USART: At least one of RX or TX should be enabled"); + } + // TODO: better calculation, including error checking and OVER8 if possible. let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; unsafe { r.brr().write_value(regs::Brr(div)); + r.cr2().write(|_w| {}); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable transceiver + w.set_te(enable_tx); + // enable receiver + w.set_re(enable_rx); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); } } From 49e109130937bd0ab0742a1a4765a97d0dc8a00c Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Fri, 28 Oct 2022 10:49:59 +0200 Subject: [PATCH 095/374] embassy-stm32: Fix bug where USART idle flag could end a `read` prematuraly --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dee466b2..e059c91b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -465,7 +465,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } - if sr.idle() { + if enable_idle_line_detection && sr.idle() { // Idle line // stop dma transfer From 0b2d6996e8f35496ee1242bf80a213eb36121a7a Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 29 Oct 2022 15:16:09 +0200 Subject: [PATCH 096/374] Fix ascii table in BootLoader doc comment Signed-off-by: Daniel Bevenius --- embassy-boot/boot/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 8286601e..429323ec 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -150,7 +150,7 @@ impl BootLoader { /// +-----------+------------+--------+--------+--------+--------+ /// | Active | 0 | 1 | 2 | 3 | - | /// | DFU | 0 | 3 | 2 | 1 | X | - /// +-----------+-------+--------+--------+--------+--------+ + /// +-----------+------------+--------+--------+--------+--------+ /// /// The algorithm starts by copying 'backwards', and after the first step, the layout is /// as follows: From 97d18c5ffb21cee4352c3ae799d762533c60e42b Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 1 Nov 2022 07:54:43 +0100 Subject: [PATCH 097/374] Move default initializer function to Default trait implementation --- embassy-boot/nrf/src/lib.rs | 6 ++- embassy-boot/stm32/src/lib.rs | 76 ++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 385e089f..82475d1e 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -17,9 +17,9 @@ pub struct BootLoader { page: AlignedBuffer, } -impl BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script - pub fn default() -> Self { + fn default() -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; @@ -54,7 +54,9 @@ impl BootLoader { Self::new(active, dfu, state) } +} +impl BootLoader { /// Create a new bootloader instance using the supplied partitions for active, dfu and state. pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index edba39cc..d549eccc 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -14,43 +14,6 @@ pub struct BootLoader { } impl BootLoader { - /// Create a new bootloader instance using parameters from linker script - pub fn default() -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_active_start: u32; - static __bootloader_active_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let active = unsafe { - Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, - ) - }; - - trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - - Self::new(active, dfu, state) - } - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { @@ -85,3 +48,42 @@ impl BootLoader Default for BootLoader { + /// Create a new bootloader instance using parameters from linker script + fn default() -> Self { + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_active_start: u32; + static __bootloader_active_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let active = unsafe { + Partition::new( + &__bootloader_active_start as *const u32 as usize, + &__bootloader_active_end as *const u32 as usize, + ) + }; + let dfu = unsafe { + Partition::new( + &__bootloader_dfu_start as *const u32 as usize, + &__bootloader_dfu_end as *const u32 as usize, + ) + }; + let state = unsafe { + Partition::new( + &__bootloader_state_start as *const u32 as usize, + &__bootloader_state_end as *const u32 as usize, + ) + }; + + trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); + trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); + trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); + + Self::new(active, dfu, state) + } +} From fc086fd4ba5679cae1bacb0556e503afd6e8894a Mon Sep 17 00:00:00 2001 From: miathedev Date: Tue, 1 Nov 2022 10:38:02 +0100 Subject: [PATCH 098/374] Add uart async example --- examples/stm32wl/src/bin/uart_async.rs | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/stm32wl/src/bin/uart_async.rs diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs new file mode 100644 index 00000000..f12fec4c --- /dev/null +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, Uart}; +use {defmt_rtt as _, panic_probe as _}; + +/* +Pass Incoming data from LPUART1 to USART1 +Example is written for the LoRa-E5 mini v1.0, +but can be surely changed for your needs. +*/ +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + let p = embassy_stm32::init(config); + + defmt::info!("Starting system"); + + let mut config1 = Config::default(); + config1.baudrate = 9600; + + let mut config2 = Config::default(); + config2.baudrate = 9600; + + //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0 + let irq = interrupt::take!(USART1); + let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH3, p.DMA1_CH4, config1); + + //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0 + let irq = interrupt::take!(LPUART1); + let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, irq, p.DMA1_CH5, p.DMA1_CH6, config2); + + unwrap!(usart1.write(b"Hello Embassy World!\r\n").await); + unwrap!(usart2.write(b"Hello Embassy World!\r\n").await); + + let mut buf = [0u8; 300]; + loop { + let result = usart2.read_until_idle(&mut buf).await; + match result { + Ok(size) => { + match usart1.write(&buf[0..size]).await { + Ok(()) => { + //Write suc. + } + Err(..) => { + //Wasnt able to write + } + } + } + Err(_err) => { + //Ignore eg. framing errors + } + } + } +} From ea4d08b6cf6f17b18b515326261f9b37e6f43856 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 1 Nov 2022 14:23:22 -0500 Subject: [PATCH 099/374] stm32-metapac-gen: Use `serde_json` to parse json files --- stm32-metapac-gen/Cargo.toml | 2 ++ stm32-metapac-gen/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml index 3c1dab57..9598a594 100644 --- a/stm32-metapac-gen/Cargo.toml +++ b/stm32-metapac-gen/Cargo.toml @@ -9,5 +9,7 @@ license = "MIT OR Apache-2.0" regex = "1.5.4" chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "28ffa8a19d84914089547f52900ffb5877a5dc23" } serde = { version = "1.0.130", features = [ "derive" ] } +serde_json = "1.0.87" serde_yaml = "0.8.21" proc-macro2 = "1.0.29" + diff --git a/stm32-metapac-gen/src/lib.rs b/stm32-metapac-gen/src/lib.rs index 9bd60cb7..64045986 100644 --- a/stm32-metapac-gen/src/lib.rs +++ b/stm32-metapac-gen/src/lib.rs @@ -223,7 +223,7 @@ impl Gen { fn load_chip(&mut self, name: &str) -> Chip { let chip_path = self.opts.data_dir.join("chips").join(&format!("{}.json", name)); let chip = fs::read(chip_path).expect(&format!("Could not load chip {}", name)); - serde_yaml::from_slice(&chip).unwrap() + serde_json::from_slice(&chip).unwrap() } pub fn gen(&mut self) { From 1920e90dcdbebc1e2f86001f1491a9f28eb0f0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 00:15:43 +0100 Subject: [PATCH 100/374] embassy-nrf: Add SPIS module --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52820.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 4 + embassy-nrf/src/chips/nrf52833.rs | 4 + embassy-nrf/src/chips/nrf52840.rs | 4 + embassy-nrf/src/chips/nrf5340_app.rs | 5 + embassy-nrf/src/chips/nrf5340_net.rs | 1 + embassy-nrf/src/chips/nrf9160.rs | 5 + embassy-nrf/src/lib.rs | 1 + embassy-nrf/src/spis.rs | 516 +++++++++++++++++++++++++++ examples/nrf/src/bin/spis.rs | 25 ++ 13 files changed, 575 insertions(+) create mode 100644 embassy-nrf/src/spis.rs create mode 100644 examples/nrf/src/bin/spis.rs diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index dec31a84..11a6840c 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -131,6 +131,8 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); +impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0); + impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); impl_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index e57a4a38..3614cd22 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -137,6 +137,8 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); +impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0); + impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); impl_pwm!(PWM0, PWM0, PWM0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 918404cf..dc4a8660 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -138,6 +138,9 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1); +impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); +impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1); + impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_pwm!(PWM0, PWM0, PWM0); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index dba033b0..7668920b 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -136,6 +136,9 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 81e66c19..851643b5 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -146,6 +146,10 @@ impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 92499e3c..5342ba8c 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -174,6 +174,10 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); impl_spim!(SPI3, SPIM3, SPIM3); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba..a330aef8 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -177,6 +177,10 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); impl_spim!(SPI3, SPIM3, SPIM3); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 7845d4a8..1c027ec0 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -361,6 +361,11 @@ impl_spim!(UARTETWISPI1, SPIM1, SERIAL1); impl_spim!(UARTETWISPI2, SPIM2, SERIAL2); impl_spim!(UARTETWISPI3, SPIM3, SERIAL3); +impl_spis!(UARTETWISPI0, SPIS0, SERIAL0); +impl_spis!(UARTETWISPI1, SPIS1, SERIAL1); +impl_spis!(UARTETWISPI2, SPIS2, SERIAL2); +impl_spis!(UARTETWISPI3, SPIS3, SERIAL3); + impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); impl_twim!(UARTETWISPI1, TWIM1, SERIAL1); impl_twim!(UARTETWISPI2, TWIM2, SERIAL2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index ae136e09..3bcd44fc 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -238,6 +238,7 @@ embassy_hal_common::peripherals! { impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); impl_spim!(UARTETWISPI0, SPIM0, SERIAL0); +impl_spis!(UARTETWISPI0, SPIS0, SERIAL0); impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); impl_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index b5a53ed8..0dfa112f 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -275,6 +275,11 @@ impl_spim!(UARTETWISPI1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_spis!(UARTETWISPI0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_spis!(UARTETWISPI1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_spis!(UARTETWISPI2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_spis!(UARTETWISPI3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f..587e19be 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -96,6 +96,7 @@ pub mod rng; #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; pub mod spim; +pub mod spis; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod temp; pub mod timer; diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs new file mode 100644 index 00000000..32c0b6f9 --- /dev/null +++ b/embassy-nrf/src/spis.rs @@ -0,0 +1,516 @@ +#![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 crate::chip::FORCE_COPY_BUFFER_SIZE; +use crate::gpio::sealed::Pin as _; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; +use crate::{pac, Peripheral}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + TxBufferTooLong, + RxBufferTooLong, + /// EasyDMA can only read from data memory, read only buffers in flash will fail. + DMABufferNotInDataMemory, +} + +/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. +pub struct Spis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +#[non_exhaustive] +pub struct Config { + pub mode: Mode, + pub orc: u8, + pub def: u8, + pub auto_acquire: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + mode: MODE_0, + orc: 0x00, + def: 0x00, + auto_acquire: true, + } + } +} + +impl<'d, T: Instance> Spis<'d, T> { + pub fn new( + spis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + miso: impl Peripheral

+ 'd, + mosi: impl Peripheral

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

+ 'd, + irq: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + mosi: impl Peripheral

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

+ 'd, + irq: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + miso: impl Peripheral

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

+ 'd, + irq: impl Peripheral

+ 'd, + cs: PeripheralRef<'d, AnyPin>, + sck: PeripheralRef<'d, AnyPin>, + miso: Option>, + mosi: Option>, + config: Config, + ) -> Self { + into_ref!(cs, spis, irq); + + let r = T::regs(); + + // Configure pins + sck.conf().write(|w| w.input().connect().drive().h0h1()); + cs.conf().write(|w| w.input().connect().drive().h0h1()); + if let Some(mosi) = &mosi { + mosi.conf().write(|w| w.input().connect().drive().h0h1()); + } + if let Some(miso) = &miso { + miso.conf().write(|w| w.dir().output().drive().h0h1()); + } + + match config.mode.polarity { + Polarity::IdleHigh => { + sck.set_high(); + if let Some(mosi) = &mosi { + mosi.set_high(); + } + } + Polarity::IdleLow => { + sck.set_low(); + if let Some(mosi) = &mosi { + mosi.set_low(); + } + } + } + + if config.auto_acquire { + r.shorts.write(|w| w.end_acquire().bit(true)); + } + + // Select pins. + r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); + r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); + r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); + r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); + + // Enable SPIS instance. + r.enable.write(|w| w.enable().enabled()); + + // Configure mode. + let mode = config.mode; + r.config.write(|w| { + match mode { + MODE_0 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().leading(); + } + MODE_1 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().trailing(); + } + MODE_2 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().leading(); + } + MODE_3 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().trailing(); + } + } + + w + }); + + // Set over-read character + let orc = config.orc; + r.orc.write(|w| unsafe { w.orc().bits(orc) }); + + // Set default character + let def = config.def; + r.def.write(|w| unsafe { w.def().bits(def) }); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self { _p: spis } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_acquired.read().bits() != 0 { + s.acquire_waker.wake(); + r.intenclr.write(|w| w.acquired().clear()); + } + } + + fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { + slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. + + compiler_fence(Ordering::SeqCst); + + let r = T::regs(); + + // Set up the DMA write. + let (ptr, len) = slice_ptr_parts(tx); + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + // Set up the DMA read. + let (ptr, len) = slice_ptr_parts_mut(rx); + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + // Reset and enable the end event + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + // Release the semaphore + r.tasks_release.write(|w| unsafe { w.bits(1) }); + + Ok(()) + } + + fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { + self.prepare(rx, tx)?; + let r = T::regs(); + + // Wait for 'end' event. + while r.events_end.read().bits() == 0 {} + + let n_rx = r.rxd.amount.read().bits() as usize; + let n_tx = r.txd.amount.read().bits() as usize; + + compiler_fence(Ordering::SeqCst); + + Ok((n_rx, n_tx)) + } + + fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { + match self.blocking_inner_from_ram(rx, tx) { + Ok(n) => Ok(n), + Err(Error::DMABufferNotInDataMemory) => { + trace!("Copying SPIS tx buffer into RAM for DMA"); + let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; + tx_ram_buf.copy_from_slice(tx); + self.blocking_inner_from_ram(rx, tx_ram_buf) + } + Err(error) => Err(error), + } + } + + async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { + let r = T::regs(); + let s = T::state(); + + if r.semstat.read().bits() != 1 { + // Reset and enable the acquire event + r.events_acquired.reset(); + r.intenset.write(|w| w.acquired().set()); + + // Requests acquiring the SPIS semaphore + r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + + // Wait for 'acquire' event. + poll_fn(|cx| { + s.acquire_waker.register(cx.waker()); + if r.events_acquired.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + } + + self.prepare(rx, tx)?; + + // Wait for 'end' event. + poll_fn(|cx| { + s.end_waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + + let n_rx = r.rxd.amount.read().bits() as usize; + let n_tx = r.txd.amount.read().bits() as usize; + + compiler_fence(Ordering::SeqCst); + + Ok((n_rx, n_tx)) + } + + async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { + match self.async_inner_from_ram(rx, tx).await { + Ok(n) => Ok(n), + Err(Error::DMABufferNotInDataMemory) => { + trace!("Copying SPIS tx buffer into RAM for DMA"); + let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; + tx_ram_buf.copy_from_slice(tx); + self.async_inner_from_ram(rx, tx_ram_buf).await + } + Err(error) => Err(error), + } + } + + /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled. + /// Returns number of bytes read + pub fn blocking_read(&mut self, data: &mut [u8]) -> Result { + self.blocking_inner(data, &[]).map(|n| n.0) + } + + /// Simultaneously sends and receives data. Blocks until the transmission is completed. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes transferred `(n_rx, n_tx)` + pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.blocking_inner(read, write) + } + + /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes transferred `(n_rx, n_tx)` + pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.blocking_inner(read, write) + } + + /// Simultaneously sends and receives data. + /// Places the received data into the same buffer and blocks until the transmission is completed. + /// Returns number of bytes transferred + pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result { + self.blocking_inner_from_ram(data, data).map(|n| n.0) + } + + /// Sends data, discarding any received data. Blocks until the transmission is completed. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes written + pub fn blocking_write(&mut self, data: &[u8]) -> Result { + self.blocking_inner(&mut [], data).map(|n| n.1) + } + + /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes written + pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result { + self.blocking_inner(&mut [], data).map(|n| n.1) + } + + /// Reads data from the SPI bus without sending anything. + /// Returns number of bytes read + pub async fn read(&mut self, data: &mut [u8]) -> Result { + self.async_inner(data, &[]).await.map(|n| n.0) + } + + /// Simultaneously sends and receives data. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes transferred `(n_rx, n_tx)` + pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.async_inner(read, write).await + } + + /// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes transferred `(n_rx, n_tx)` + pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.async_inner_from_ram(read, write).await + } + + /// Simultaneously sends and receives data. Places the received data into the same buffer. + /// Returns number of bytes transferred + pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result { + self.async_inner_from_ram(data, data).await.map(|n| n.0) + } + + /// Sends data, discarding any received data. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes written + pub async fn write(&mut self, data: &[u8]) -> Result { + self.async_inner(&mut [], data).await.map(|n| n.1) + } + + /// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes written + pub async fn write_from_ram(&mut self, data: &[u8]) -> Result { + self.async_inner_from_ram(&mut [], data).await.map(|n| n.1) + } +} + +impl<'d, T: Instance> Drop for Spis<'d, T> { + fn drop(&mut self) { + trace!("spis drop"); + + // Disable + let r = T::regs(); + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.sck.read().bits()); + gpio::deconfigure_pin(r.psel.csn.read().bits()); + gpio::deconfigure_pin(r.psel.miso.read().bits()); + gpio::deconfigure_pin(r.psel.mosi.read().bits()); + + trace!("spis drop: done"); + } +} + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + use super::*; + + pub struct State { + pub end_waker: AtomicWaker, + pub acquire_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + end_waker: AtomicWaker::new(), + acquire_waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static pac::spis0::RegisterBlock; + fn state() -> &'static State; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + 'static { + type Interrupt: Interrupt; +} + +macro_rules! impl_spis { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::spis::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::spis0::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::spis::sealed::State { + static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new(); + &STATE + } + } + impl crate::spis::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} + +// ==================== + +impl<'d, T: Instance> SetConfig for Spis<'d, T> { + type Config = Config; + fn set_config(&mut self, config: &Self::Config) { + let r = T::regs(); + // Configure mode. + let mode = config.mode; + r.config.write(|w| { + match mode { + MODE_0 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().leading(); + } + MODE_1 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().trailing(); + } + MODE_2 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().leading(); + } + MODE_3 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().trailing(); + } + } + + w + }); + + // Set over-read character + let orc = config.orc; + r.orc.write(|w| unsafe { w.orc().bits(orc) }); + + // Set default character + let def = config.def; + r.def.write(|w| unsafe { w.def().bits(def) }); + + // Set auto acquire + let auto_acquire = config.auto_acquire; + r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); + + } +} diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs new file mode 100644 index 00000000..181e0840 --- /dev/null +++ b/examples/nrf/src/bin/spis.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::spis::{self, Config}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Running!"); + + let irq = interrupt::take!(SPIM2_SPIS2_SPI2); + let mut spis = spis::Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + + loop { + let mut buf = [0_u8; 64]; + if let Ok(n) = spis.read(&mut buf).await { + info!("RX: {:?}", buf[..n]); + } + } +} From a3e8a6bc3a706bc59b9d017699eaab93c1ba60d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 00:19:52 +0100 Subject: [PATCH 101/374] rustfmt --- embassy-nrf/src/spis.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 32c0b6f9..ab7986b8 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -82,7 +82,15 @@ impl<'d, T: Instance> Spis<'d, T> { config: Config, ) -> Self { into_ref!(cs, sck, mosi); - Self::new_inner(spis, irq, cs.map_into(), sck.map_into(), None, Some(mosi.map_into()), config) + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + None, + Some(mosi.map_into()), + config, + ) } pub fn new_rxonly( @@ -94,7 +102,15 @@ impl<'d, T: Instance> Spis<'d, T> { config: Config, ) -> Self { into_ref!(cs, sck, miso); - Self::new_inner(spis, irq, cs.map_into(), sck.map_into(), Some(miso.map_into()), None, config) + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + Some(miso.map_into()), + None, + config, + ) } fn new_inner( @@ -278,7 +294,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Requests acquiring the SPIS semaphore r.tasks_acquire.write(|w| unsafe { w.bits(1) }); - + // Wait for 'acquire' event. poll_fn(|cx| { s.acquire_waker.register(cx.waker()); @@ -511,6 +527,5 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> { // Set auto acquire let auto_acquire = config.auto_acquire; r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); - } } From 7da18e194a8a9fef207803b96b16e7b7bc787ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:12:25 +0100 Subject: [PATCH 102/374] Add status checks --- embassy-nrf/src/spis.rs | 69 ++++++++++++++++++++++-------------- examples/nrf/src/bin/spis.rs | 4 +-- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index ab7986b8..71106b7d 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -126,7 +126,7 @@ impl<'d, T: Instance> Spis<'d, T> { let r = T::regs(); - // Configure pins + // Configure pins. sck.conf().write(|w| w.input().connect().drive().h0h1()); cs.conf().write(|w| w.input().connect().drive().h0h1()); if let Some(mosi) = &mosi { @@ -151,10 +151,6 @@ impl<'d, T: Instance> Spis<'d, T> { } } - if config.auto_acquire { - r.shorts.write(|w| w.end_acquire().bit(true)); - } - // Select pins. r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); @@ -193,15 +189,20 @@ impl<'d, T: Instance> Spis<'d, T> { w }); - // Set over-read character + // Set over-read character. let orc = config.orc; r.orc.write(|w| unsafe { w.orc().bits(orc) }); - // Set default character + // Set default character. let def = config.def; r.def.write(|w| unsafe { w.def().bits(def) }); - // Disable all events interrupts + // Configure auto-acquire on 'transfer end' event. + if config.auto_acquire { + r.shorts.write(|w| w.end_acquire().bit(true)); + } + + // Disable all events interrupts. r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); irq.set_handler(Self::on_interrupt); @@ -245,11 +246,11 @@ impl<'d, T: Instance> Spis<'d, T> { r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - // Reset and enable the end event + // Reset and enable the end event. r.events_end.reset(); r.intenset.write(|w| w.end().set()); - // Release the semaphore + // Release the semaphore. r.tasks_release.write(|w| unsafe { w.bits(1) }); Ok(()) @@ -287,12 +288,15 @@ impl<'d, T: Instance> Spis<'d, T> { let r = T::regs(); let s = T::state(); + // Clear status register. + r.status.write(|w| w.overflow().clear().overread().clear()); + if r.semstat.read().bits() != 1 { - // Reset and enable the acquire event + // Reset and enable the acquire event. r.events_acquired.reset(); r.intenset.write(|w| w.acquired().set()); - // Requests acquiring the SPIS semaphore + // Request acquiring the SPIS semaphore. r.tasks_acquire.write(|w| unsafe { w.bits(1) }); // Wait for 'acquire' event. @@ -341,81 +345,92 @@ impl<'d, T: Instance> Spis<'d, T> { } /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled. - /// Returns number of bytes read + /// Returns number of bytes read. pub fn blocking_read(&mut self, data: &mut [u8]) -> Result { self.blocking_inner(data, &[]).map(|n| n.0) } /// Simultaneously sends and receives data. Blocks until the transmission is completed. /// If necessary, the write buffer will be copied into RAM (see struct description for detail). - /// Returns number of bytes transferred `(n_rx, n_tx)` + /// Returns number of bytes transferred `(n_rx, n_tx)`. pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { self.blocking_inner(read, write) } /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - /// Returns number of bytes transferred `(n_rx, n_tx)` + /// Returns number of bytes transferred `(n_rx, n_tx)`. pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { self.blocking_inner(read, write) } /// Simultaneously sends and receives data. /// Places the received data into the same buffer and blocks until the transmission is completed. - /// Returns number of bytes transferred + /// Returns number of bytes transferred. pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result { self.blocking_inner_from_ram(data, data).map(|n| n.0) } /// Sends data, discarding any received data. Blocks until the transmission is completed. /// If necessary, the write buffer will be copied into RAM (see struct description for detail). - /// Returns number of bytes written + /// Returns number of bytes written. pub fn blocking_write(&mut self, data: &[u8]) -> Result { self.blocking_inner(&mut [], data).map(|n| n.1) } /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - /// Returns number of bytes written + /// Returns number of bytes written. pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result { self.blocking_inner(&mut [], data).map(|n| n.1) } /// Reads data from the SPI bus without sending anything. - /// Returns number of bytes read + /// Returns number of bytes read. pub async fn read(&mut self, data: &mut [u8]) -> Result { self.async_inner(data, &[]).await.map(|n| n.0) } /// Simultaneously sends and receives data. /// If necessary, the write buffer will be copied into RAM (see struct description for detail). - /// Returns number of bytes transferred `(n_rx, n_tx)` + /// Returns number of bytes transferred `(n_rx, n_tx)`. pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { self.async_inner(read, write).await } /// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - /// Returns number of bytes transferred `(n_rx, n_tx)` + /// Returns number of bytes transferred `(n_rx, n_tx)`. pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { self.async_inner_from_ram(read, write).await } /// Simultaneously sends and receives data. Places the received data into the same buffer. - /// Returns number of bytes transferred + /// Returns number of bytes transferred. pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result { self.async_inner_from_ram(data, data).await.map(|n| n.0) } /// Sends data, discarding any received data. /// If necessary, the write buffer will be copied into RAM (see struct description for detail). - /// Returns number of bytes written + /// Returns number of bytes written. pub async fn write(&mut self, data: &[u8]) -> Result { self.async_inner(&mut [], data).await.map(|n| n.1) } /// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - /// Returns number of bytes written + /// Returns number of bytes written. pub async fn write_from_ram(&mut self, data: &[u8]) -> Result { self.async_inner_from_ram(&mut [], data).await.map(|n| n.1) } + + /// Checks if last transaction overread. + pub fn is_overread(&mut self) -> bool { + T::regs().status.read().overread().is_present() + } + + /// Checks if last transaction overflowed. + pub fn is_overflow(&mut self) -> bool { + T::regs().status.read().overflow().is_present() + } + } impl<'d, T: Instance> Drop for Spis<'d, T> { @@ -516,15 +531,15 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> { w }); - // Set over-read character + // Set over-read character. let orc = config.orc; r.orc.write(|w| unsafe { w.orc().bits(orc) }); - // Set default character + // Set default character. let def = config.def; r.def.write(|w| unsafe { w.def().bits(def) }); - // Set auto acquire + // Configure auto-acquire on 'transfer end' event. let auto_acquire = config.auto_acquire; r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); } diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs index 181e0840..0fce23d3 100644 --- a/examples/nrf/src/bin/spis.rs +++ b/examples/nrf/src/bin/spis.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::spis::{self, Config}; +use embassy_nrf::spis::{Spis, Config}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { info!("Running!"); let irq = interrupt::take!(SPIM2_SPIS2_SPI2); - let mut spis = spis::Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); loop { let mut buf = [0_u8; 64]; From 207fa195512c9e6604a1dec80ec055b06b9b49dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:34:52 +0100 Subject: [PATCH 103/374] Acquire semaphore on blocking --- embassy-nrf/src/spis.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 71106b7d..22c13557 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -257,9 +257,19 @@ impl<'d, T: Instance> Spis<'d, T> { } fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { - self.prepare(rx, tx)?; + compiler_fence(Ordering::SeqCst); let r = T::regs(); + // Acquire semaphore. + if r.semstat.read().bits() != 1 { + r.events_acquired.reset(); + r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + // Wait until CPU has acquired the semaphore. + while r.semstat.read().bits() != 1 {} + } + + self.prepare(rx, tx)?; + // Wait for 'end' event. while r.events_end.read().bits() == 0 {} @@ -291,6 +301,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Clear status register. r.status.write(|w| w.overflow().clear().overread().clear()); + // Acquire semaphore. if r.semstat.read().bits() != 1 { // Reset and enable the acquire event. r.events_acquired.reset(); @@ -299,10 +310,10 @@ impl<'d, T: Instance> Spis<'d, T> { // Request acquiring the SPIS semaphore. r.tasks_acquire.write(|w| unsafe { w.bits(1) }); - // Wait for 'acquire' event. + // Wait until CPU has acquired the semaphore. poll_fn(|cx| { s.acquire_waker.register(cx.waker()); - if r.events_acquired.read().bits() != 0 { + if r.semstat.read().bits() == 1 { return Poll::Ready(()); } Poll::Pending From aecfce1159480610586bc60cb90af712fae782c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:36:29 +0100 Subject: [PATCH 104/374] rustfmt --- embassy-nrf/src/spis.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 22c13557..3f77c61d 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -441,7 +441,6 @@ impl<'d, T: Instance> Spis<'d, T> { pub fn is_overflow(&mut self) -> bool { T::regs().status.read().overflow().is_present() } - } impl<'d, T: Instance> Drop for Spis<'d, T> { From af34fc4ccc110bf37165f2b9655585ba3a33889a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:40:20 +0100 Subject: [PATCH 105/374] rustfmt --- examples/nrf/src/bin/spis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs index 0fce23d3..dade5fcb 100644 --- a/examples/nrf/src/bin/spis.rs +++ b/examples/nrf/src/bin/spis.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::spis::{Spis, Config}; +use embassy_nrf::spis::{Config, Spis}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 14a2d1524080593f7795fe14950a3f0ee6e2b409 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 5 Nov 2022 22:55:04 +0800 Subject: [PATCH 106/374] Derive Default for WakerRegistration This simplifies creating arrays of WakerRegistrations --- embassy-sync/src/waitqueue/waker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/waitqueue/waker.rs b/embassy-sync/src/waitqueue/waker.rs index 64e300eb..9ce94a08 100644 --- a/embassy-sync/src/waitqueue/waker.rs +++ b/embassy-sync/src/waitqueue/waker.rs @@ -6,7 +6,7 @@ use crate::blocking_mutex::raw::CriticalSectionRawMutex; use crate::blocking_mutex::Mutex; /// Utility struct to register and wake a waker. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct WakerRegistration { waker: Option, } From 1365ce6ab88d4891ddad945fae9f71043f521fb6 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Mon, 7 Nov 2022 17:46:32 +0100 Subject: [PATCH 107/374] embassy-stm32: Fix bug when Uart::read future is dropped and DMA request was not stopped fixes issue #1045 regression was introduced with PR #1031 --- embassy-stm32/src/usart/mod.rs | 164 ++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 64 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0a1ea94e..aea054a4 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -6,6 +6,8 @@ use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; +use embassy_futures::select::{select, Either}; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::NoDma; @@ -85,6 +87,13 @@ pub enum Error { BufferTooLong, } +enum ReadCompletionEvent { + // DMA Read transfer completed first + DmaCompleted, + // Idle line detected first + Idle, +} + pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { tx: UartTx<'d, T, TxDma>, rx: UartRx<'d, T, RxDma>, @@ -385,30 +394,50 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { self.inner_read(buffer, true).await } - async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + async fn inner_read_run( + &mut self, + buffer: &mut [u8], + enable_idle_line_detection: bool, + ) -> Result where RxDma: crate::usart::RxDma, { - if buffer.is_empty() { - return Ok(0); - } else if buffer.len() > 0xFFFF { - return Err(Error::BufferTooLong); - } - let r = T::regs(); - let buffer_len = buffer.len(); + // make sure USART state is restored to neutral state when this future is dropped + let _drop = OnDrop::new(move || { + // defmt::trace!("Clear all USART interrupts and DMA Read Request"); + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + }); let ch = &mut self.rx_dma; let request = ch.request(); + // Start USART DMA + // will not do anything yet because DMAR is not yet set + // future which will complete when DMA Read request completes + let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); + // SAFETY: The only way we might have a problem is using split rx and tx // here we only modify or read Rx related flags, interrupts and DMA channel unsafe { - // Start USART DMA - // will not do anything yet because DMAR is not yet set - ch.start_read(request, rdr(r), buffer, Default::default()); - // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer if !self.detect_previous_overrun { let sr = sr(r).read(); @@ -443,9 +472,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // something went wrong // because the only way to get this flag cleared is to have an interrupt - // abort DMA transfer - ch.request_stop(); - while ch.is_running() {} + // DMA will be stopped when transfer is dropped let sr = sr(r).read(); // This read also clears the error and idle interrupt flags on v1. @@ -468,26 +495,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { unreachable!(); } - // clear idle flag - if enable_idle_line_detection { - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); + if !enable_idle_line_detection { + transfer.await; - // enable idle interrupt - r.cr1().modify(|w| { - w.set_idleie(true); - }); + return Ok(ReadCompletionEvent::DmaCompleted); } + + // clear idle flag + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); } compiler_fence(Ordering::SeqCst); - let res = poll_fn(move |cx| { + // future which completes when idle line is detected + let idle = poll_fn(move |cx| { let s = T::state(); - ch.set_waker(cx.waker()); s.rx_waker.register(cx.waker()); // SAFETY: read only and we only use Rx related flags @@ -507,10 +538,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { if has_errors { // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler - // stop dma transfer - ch.request_stop(); - while ch.is_running() {} - if sr.pe() { return Poll::Ready(Err(Error::Parity)); } @@ -525,45 +552,54 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } - if enable_idle_line_detection && sr.idle() { - // Idle line - - // stop dma transfer - ch.request_stop(); - while ch.is_running() {} - - let n = buffer_len - (ch.remaining_transfers() as usize); - - return Poll::Ready(Ok(n)); - } else if !ch.is_running() { - // DMA complete - return Poll::Ready(Ok(buffer_len)); + if sr.idle() { + // Idle line detected + return Poll::Ready(Ok(())); } Poll::Pending - }) - .await; + }); - // clear all interrupts and DMA Rx Request - // SAFETY: only clears Rx related flags - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); + // wait for the first of DMA request or idle line detected to completes + // select consumes its arguments + // when transfer is dropped, it will stop the DMA request + match select(transfer, idle).await { + // DMA transfer completed first + Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), + + // Idle line detected first + Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle), + + // error occurred + Either::Second(Err(e)) => Err(e), + } + } + + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + where + RxDma: crate::usart::RxDma, + { + if buffer.is_empty() { + return Ok(0); + } else if buffer.len() > 0xFFFF { + return Err(Error::BufferTooLong); } - res + let buffer_len = buffer.len(); + + // wait for DMA to complete or IDLE line detection if requested + let res = self.inner_read_run(buffer, enable_idle_line_detection).await; + + let ch = &mut self.rx_dma; + + match res { + Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), + Ok(ReadCompletionEvent::Idle) => { + let n = buffer_len - (ch.remaining_transfers() as usize); + Ok(n) + } + Err(e) => Err(e), + } } } From bd5ef80bec42c91cb1b4fd50ea4bbc12eac85283 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 7 Nov 2022 20:51:29 +0100 Subject: [PATCH 108/374] Ensure embassy-lora stm32wl supports log crate --- embassy-lora/src/stm32wl/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 8d5d1953..08bf32f9 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -70,7 +70,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form /// the upcoming RX window start. async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX request: {}", config); + trace!("TX request: {:?}", config); self.switch.set_tx(); self.radio @@ -130,7 +130,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// be able to hold a single LoRaWAN packet. async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { assert!(buf.len() >= 255); - trace!("RX request: {}", config); + trace!("RX request: {:?}", config); self.switch.set_rx(); self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; @@ -172,7 +172,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.read_buffer(ptr, &mut buf[..len as usize])?; self.radio.set_standby(StandbyClk::Rc)?; - trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); + trace!("RX done: {:02x?}", &mut buf[..len as usize]); return Ok((len as usize, RxQuality::new(rssi, snr as i8))); } @@ -193,7 +193,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { .clear_irq_status(irq_status) .expect("error clearing irq status"); - trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status); + trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status); if irq_status == 0 { Poll::Pending From a3a58e8e4a1f27402c0800356ec5bb5bf39478ec Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 9 Nov 2022 10:04:37 +0100 Subject: [PATCH 109/374] Special handling for log and defmt --- embassy-lora/src/stm32wl/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 08bf32f9..6ed63bf7 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -172,6 +172,10 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.read_buffer(ptr, &mut buf[..len as usize])?; self.radio.set_standby(StandbyClk::Rc)?; + #[cfg(feature = "defmt")] + trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); + + #[cfg(feature = "log")] trace!("RX done: {:02x?}", &mut buf[..len as usize]); return Ok((len as usize, RxQuality::new(rssi, snr as i8))); } From cecd77938c694ff2bad2a259ff64f2f468dcb04a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:14:43 +0100 Subject: [PATCH 110/374] Draft: Initial support for I2S with a working example. Co-authored-by: @brainstorm --- embassy-nrf/Cargo.toml | 1 + embassy-nrf/src/chips/nrf52840.rs | 5 + embassy-nrf/src/i2s.rs | 403 ++++++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 2 + examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/i2s.rs | 48 ++++ 6 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 embassy-nrf/src/i2s.rs create mode 100644 examples/nrf/src/bin/i2s.rs diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 67b6bec4..aa1576fd 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -48,6 +48,7 @@ nrf9160-s = ["_nrf9160"] nrf9160-ns = ["_nrf9160"] gpiote = [] +i2s = [] time-driver-rtc1 = ["_time-driver"] # Features starting with `_` are for internal use only. They're not intended diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba..cf800c7b 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -164,6 +164,9 @@ embassy_hal_common::peripherals! { // PDM PDM, + + // I2S + I2S, } #[cfg(feature = "nightly")] @@ -285,6 +288,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_i2s!(I2S, I2S, I2S); + pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs new file mode 100644 index 00000000..0199ac61 --- /dev/null +++ b/embassy-nrf/src/i2s.rs @@ -0,0 +1,403 @@ +#![macro_use] + +//! I2S + +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 pac::i2s::config::mcken; + +use crate::{pac, Peripheral}; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; +use crate::gpio::sealed::Pin as _; + +// TODO: Define those in lib.rs somewhere else +// +// I2S EasyDMA MAXCNT bit length = 14 +const MAX_DMA_MAXCNT: u32 = 1 << 14; + +// Limits for Easy DMA - it can only read from data ram +pub const SRAM_LOWER: usize = 0x2000_0000; +pub const SRAM_UPPER: usize = 0x3000_0000; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + BufferTooLong, + BufferZeroLength, + DMABufferNotInDataMemory, + BufferMisaligned, + // TODO: add other error variants. +} + +#[derive(Clone)] +#[non_exhaustive] +pub struct Config { + pub ratio: Ratio, + pub sample_width: SampleWidth, + pub align: Align, + pub format: Format, + pub channels: Channels, +} + +impl Default for Config { + fn default() -> Self { + Self { + ratio: Ratio::_32x, + sample_width: SampleWidth::_16bit, + align: Align::Left, + format: Format::I2S, + channels: Channels::Stereo, + } + } +} + +/// MCK / LRCK ratio. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Ratio { + _32x, + _48x, + _64x, + _96x, + _128x, + _192x, + _256x, + _384x, + _512x, +} + +impl From for u8 { + fn from(variant: Ratio) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum SampleWidth { + _8bit, + _16bit, + _24bit, +} + +impl From for u8 { + fn from(variant: SampleWidth) -> Self { + variant as _ + } +} + +/// Alignment of sample within a frame. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Align { + Left, + Right, +} + +impl From for bool { + fn from(variant: Align) -> Self { + match variant { + Align::Left => false, + Align::Right => true, + } + } +} + +/// Frame format. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Format { + I2S, + Aligned, +} + +impl From for bool { + fn from(variant: Format) -> Self { + match variant { + Format::I2S => false, + Format::Aligned => true, + } + } +} + +/// Enable channels. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Channels { + Stereo, + Left, + Right, +} + +impl From for u8 { + fn from(variant: Channels) -> Self { + variant as _ + } +} + +/// I2S Mode +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Mode { + Controller, + Peripheral, +} + +// /// Master clock generator frequency. +// #[derive(Debug, Eq, PartialEq, Clone, Copy)] +// pub enum MckFreq { +// _32MDiv8 = 0x20000000, +// _32MDiv10 = 0x18000000, +// _32MDiv11 = 0x16000000, +// _32MDiv15 = 0x11000000, +// _32MDiv16 = 0x10000000, +// _32MDiv21 = 0x0C000000, +// _32MDiv23 = 0x0B000000, +// _32MDiv30 = 0x08800000, +// _32MDiv31 = 0x08400000, +// _32MDiv32 = 0x08000000, +// _32MDiv42 = 0x06000000, +// _32MDiv63 = 0x04100000, +// _32MDiv125 = 0x020C0000, +// } + + +/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. +pub struct I2s<'d, T: Instance> { + output: I2sOutput<'d, T>, + input: I2sInput<'d, T>, +} + +/// Transmitter interface to the UARTE peripheral obtained +/// via [Uarte]::split. +pub struct I2sOutput<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +/// Receiver interface to the UARTE peripheral obtained +/// via [Uarte]::split. +pub struct I2sInput<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> I2s<'d, T> { + /// Create a new I2S + pub fn new( + i2s: impl Peripheral

+ 'd, + // irq: impl Peripheral

+ 'd, + mck: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + lrck: impl Peripheral

+ 'd, + sdin: impl Peripheral

+ 'd, + sdout: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(mck, sck, lrck, sdin, sdout); + Self::new_inner( + i2s, + // irq, + mck.map_into(), sck.map_into(), lrck.map_into(), sdin.map_into(), sdout.map_into(), config) + } + + fn new_inner( + i2s: impl Peripheral

+ 'd, + // irq: impl Peripheral

+ 'd, + mck: PeripheralRef<'d, AnyPin>, + sck: PeripheralRef<'d, AnyPin>, + lrck: PeripheralRef<'d, AnyPin>, + sdin: PeripheralRef<'d, AnyPin>, + sdout: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!( + i2s, + // irq, + mck, sck, lrck, sdin, sdout); + + let r = T::regs(); + + // TODO get configuration rather than hardcoding ratio, swidth, align, format, channels + + r.config.mcken.write(|w| w.mcken().enabled()); + r.config.mckfreq.write(|w| w.mckfreq()._32mdiv16()); + r.config.ratio.write(|w| w.ratio()._192x()); + r.config.mode.write(|w| w.mode().master()); + r.config.swidth.write(|w| w.swidth()._16bit()); + r.config.align.write(|w| w.align().left()); + r.config.format.write(|w| w.format().i2s()); + r.config.channels.write(|w| w.channels().stereo()); + + r.psel.mck.write(|w| { + unsafe { w.bits(mck.psel_bits()) }; + w.connect().connected() + }); + + r.psel.sck.write(|w| { + unsafe { w.bits(sck.psel_bits()) }; + w.connect().connected() + }); + + r.psel.lrck.write(|w| { + unsafe { w.bits(lrck.psel_bits()) }; + w.connect().connected() + }); + + r.psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + + r.psel.sdout.write(|w| { + unsafe { w.bits(sdout.psel_bits()) }; + w.connect().connected() + }); + + r.enable.write(|w| w.enable().enabled()); + + Self { + output: I2sOutput { + _p: unsafe { i2s.clone_unchecked() }, + }, + input: I2sInput { _p: i2s }, + } + } + + /// Enables the I2S module. + #[inline(always)] + pub fn enable(&self) -> &Self { + let r = T::regs(); + r.enable.write(|w| w.enable().enabled()); + self + } + + /// Disables the I2S module. + #[inline(always)] + pub fn disable(&self) -> &Self { + let r = T::regs(); + r.enable.write(|w| w.enable().disabled()); + self + } + + /// Starts I2S transfer. + #[inline(always)] + pub fn start(&self) -> &Self { + let r = T::regs(); + self.enable(); + r.tasks_start.write(|w| unsafe { w.bits(1) }); + self + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub fn stop(&self) -> &Self { + todo!() + } + + /// Enables/disables I2S transmission (TX). + #[inline(always)] + pub fn set_tx_enabled(&self, enabled: bool) -> &Self { + let r = T::regs(); + r.config.txen.write(|w| w.txen().bit(enabled)); + self + } + + /// Enables/disables I2S reception (RX). + #[inline(always)] + pub fn set_rx_enabled(&self, enabled: bool) -> &Self { + let r = T::regs(); + r.config.rxen.write(|w| w.rxen().bit(enabled)); + self + } + + /// Transmits the given `tx_buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + // TODO Define a better interface for the input buffer + #[allow(unused_mut)] + pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { + self.output.tx(ptr, len).await + } +} + +impl<'d, T: Instance> I2sOutput<'d, T> { + /// Transmits the given `tx_buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + // TODO Define a better interface for the input buffer + pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { + if ptr as u32 % 4 != 0 { + return Err(Error::BufferMisaligned); + } + let maxcnt = (len / (core::mem::size_of::() / core::mem::size_of::())) as u32; + if maxcnt > MAX_DMA_MAXCNT { + return Err(Error::BufferTooLong); + } + if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + return Err(Error::DMABufferNotInDataMemory); + } + + let r = T::regs(); + let _s = T::state(); + + // TODO we can not progress until the last buffer written in TXD.PTR + // has started the transmission. + // We can use some sync primitive from `embassy-sync`. + + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + + Ok(()) + } +} + +pub(crate) mod sealed { + use core::sync::atomic::AtomicU8; + + use embassy_sync::waitqueue::AtomicWaker; + + use super::*; + + pub struct State { + pub input_waker: AtomicWaker, + pub output_waker: AtomicWaker, + pub buffers_refcount: AtomicU8, + } + impl State { + pub const fn new() -> Self { + Self { + input_waker: AtomicWaker::new(), + output_waker: AtomicWaker::new(), + buffers_refcount: AtomicU8::new(0), + } + } + } + + pub trait Instance { + fn regs() -> &'static pac::i2s::RegisterBlock; + fn state() -> &'static State; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + type Interrupt: Interrupt; +} + +macro_rules! impl_i2s { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::i2s::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::i2s::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::i2s::sealed::State { + static STATE: crate::i2s::sealed::State = crate::i2s::sealed::State::new(); + &STATE + } + } + impl crate::i2s::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} + diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f..ac797db9 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,6 +74,8 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; +// #[cfg(all(feature = "i2s", feature = "nrf52840"))] +pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; #[cfg(any( diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index c633f82f..a79044e8 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "i2s", "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 } embedded-io = "0.3.1" diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs new file mode 100644 index 00000000..556f6b2e --- /dev/null +++ b/examples/nrf/src/bin/i2s.rs @@ -0,0 +1,48 @@ +// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{i2s}; +use {defmt_rtt as _, panic_probe as _}; + +#[repr(align(4))] +pub struct Aligned(T); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let config = i2s::Config::default(); + + let mut i2s = i2s::I2s::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + + let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); + let len = signal_buf.0.len() / 2; + for x in 0..len { + signal_buf.0[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; + signal_buf.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; + } + + let ptr = &signal_buf.0 as *const i16 as *const u8; + let len = signal_buf.0.len() * core::mem::size_of::(); + + i2s.start(); + i2s.set_tx_enabled(true); + + loop { + i2s.tx(ptr, len).await; + } +} + +fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 { + let length = length as i32; + amplitude + - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) + % (2 * amplitude) + - amplitude) + .abs() + - amplitude / 2 +} From 3760b60db382c8c4f8d7067a8d472affa6db928b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 9 Nov 2022 21:58:56 +0100 Subject: [PATCH 111/374] Make bors grin ;) --- embassy-nrf/src/i2s.rs | 47 +++++++++++++++++++++---------------- examples/nrf/src/bin/i2s.rs | 17 +++++++------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 0199ac61..e0fe6a6e 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -2,18 +2,19 @@ //! I2S -use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::Poll; +//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::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use pac::i2s::config::mcken; -use crate::{pac, Peripheral}; -use crate::interrupt::{Interrupt, InterruptExt}; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::gpio::sealed::Pin as _; +//use crate::pac::i2s::config::mcken; + +//use crate::gpio::sealed::Pin as _; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::Interrupt; +use crate::Peripheral; // TODO: Define those in lib.rs somewhere else // @@ -161,13 +162,12 @@ pub enum Mode { // _32MDiv125 = 0x020C0000, // } - /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. pub struct I2s<'d, T: Instance> { output: I2sOutput<'d, T>, - input: I2sInput<'d, T>, + _input: I2sInput<'d, T>, } /// Transmitter interface to the UARTE peripheral obtained @@ -198,7 +198,13 @@ impl<'d, T: Instance> I2s<'d, T> { Self::new_inner( i2s, // irq, - mck.map_into(), sck.map_into(), lrck.map_into(), sdin.map_into(), sdout.map_into(), config) + mck.map_into(), + sck.map_into(), + lrck.map_into(), + sdin.map_into(), + sdout.map_into(), + config, + ) } fn new_inner( @@ -209,12 +215,12 @@ impl<'d, T: Instance> I2s<'d, T> { lrck: PeripheralRef<'d, AnyPin>, sdin: PeripheralRef<'d, AnyPin>, sdout: PeripheralRef<'d, AnyPin>, - config: Config, + _config: Config, ) -> Self { into_ref!( - i2s, - // irq, - mck, sck, lrck, sdin, sdout); + i2s, // irq, + mck, sck, lrck, sdin, sdout + ); let r = T::regs(); @@ -260,7 +266,7 @@ impl<'d, T: Instance> I2s<'d, T> { output: I2sOutput { _p: unsafe { i2s.clone_unchecked() }, }, - input: I2sInput { _p: i2s }, + _input: I2sInput { _p: i2s }, } } @@ -357,7 +363,7 @@ pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; - use super::*; + //use super::*; pub struct State { pub input_waker: AtomicWaker, @@ -375,7 +381,7 @@ pub(crate) mod sealed { } pub trait Instance { - fn regs() -> &'static pac::i2s::RegisterBlock; + fn regs() -> &'static crate::pac::i2s::RegisterBlock; fn state() -> &'static State; } } @@ -384,6 +390,8 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { type Interrupt: Interrupt; } +// TODO: Unsure why this macro is flagged as unused by CI when in fact it's used elsewhere? +#[allow(unused_macros)] macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::sealed::Instance for peripherals::$type { @@ -400,4 +408,3 @@ macro_rules! impl_i2s { } }; } - diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 556f6b2e..60cde3b6 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -4,9 +4,9 @@ #![no_main] #![feature(type_alias_impl_trait)] -use defmt::*; +//use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{i2s}; +use embassy_nrf::i2s; use {defmt_rtt as _, panic_probe as _}; #[repr(align(4))] @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { let config = i2s::Config::default(); let mut i2s = i2s::I2s::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); - + let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); let len = signal_buf.0.len() / 2; for x in 0..len { @@ -31,18 +31,19 @@ async fn main(_spawner: Spawner) { i2s.start(); i2s.set_tx_enabled(true); - + loop { - i2s.tx(ptr, len).await; + match i2s.tx(ptr, len).await { + Ok(_) => todo!(), + Err(_) => todo!(), + }; } } fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 { let length = length as i32; amplitude - - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) - % (2 * amplitude) - - amplitude) + - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) % (2 * amplitude) - amplitude) .abs() - amplitude / 2 } From 356beabc3b11d78612c6958d1cfe542209e43558 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Wed, 9 Nov 2022 22:47:55 +0100 Subject: [PATCH 112/374] Apply config --- embassy-nrf/src/i2s.rs | 85 ++++++++++++++++++++++++++++--------- examples/nrf/src/bin/i2s.rs | 2 +- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index e0fe6a6e..8752dfde 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -9,11 +9,10 @@ //use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -//use crate::pac::i2s::config::mcken; - //use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; +use crate::pac::i2s::CONFIG; use crate::Peripheral; // TODO: Define those in lib.rs somewhere else @@ -40,7 +39,7 @@ pub enum Error { #[non_exhaustive] pub struct Config { pub ratio: Ratio, - pub sample_width: SampleWidth, + pub swidth: SampleWidth, pub align: Align, pub format: Format, pub channels: Channels, @@ -50,7 +49,7 @@ impl Default for Config { fn default() -> Self { Self { ratio: Ratio::_32x, - sample_width: SampleWidth::_16bit, + swidth: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, channels: Channels::Stereo, @@ -165,7 +164,7 @@ pub enum Mode { /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. -pub struct I2s<'d, T: Instance> { +pub struct I2S<'d, T: Instance> { output: I2sOutput<'d, T>, _input: I2sInput<'d, T>, } @@ -182,7 +181,7 @@ pub struct I2sInput<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> I2s<'d, T> { +impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S pub fn new( i2s: impl Peripheral

+ 'd, @@ -215,25 +214,13 @@ impl<'d, T: Instance> I2s<'d, T> { lrck: PeripheralRef<'d, AnyPin>, sdin: PeripheralRef<'d, AnyPin>, sdout: PeripheralRef<'d, AnyPin>, - _config: Config, + config: Config, ) -> Self { - into_ref!( - i2s, // irq, - mck, sck, lrck, sdin, sdout - ); + into_ref!(i2s, /* irq, */ mck, sck, lrck, sdin, sdout); let r = T::regs(); - // TODO get configuration rather than hardcoding ratio, swidth, align, format, channels - - r.config.mcken.write(|w| w.mcken().enabled()); - r.config.mckfreq.write(|w| w.mckfreq()._32mdiv16()); - r.config.ratio.write(|w| w.ratio()._192x()); - r.config.mode.write(|w| w.mode().master()); - r.config.swidth.write(|w| w.swidth()._16bit()); - r.config.align.write(|w| w.align().left()); - r.config.format.write(|w| w.format().i2s()); - r.config.channels.write(|w| w.channels().stereo()); + Self::apply_config(&r.config, &config); r.psel.mck.write(|w| { unsafe { w.bits(mck.psel_bits()) }; @@ -325,6 +312,62 @@ impl<'d, T: Instance> I2s<'d, T> { pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { self.output.tx(ptr, len).await } + + fn apply_config(c: &CONFIG, config: &Config) { + // TODO support slave too + c.mcken.write(|w| w.mcken().enabled()); + c.mckfreq.write(|w| w.mckfreq()._32mdiv16()); + c.mode.write(|w| w.mode().master()); + + c.ratio.write(|w| { + let ratio = w.ratio(); + match config.ratio { + Ratio::_32x => ratio._32x(), + Ratio::_48x => ratio._48x(), + Ratio::_64x => ratio._64x(), + Ratio::_96x => ratio._96x(), + Ratio::_128x => ratio._128x(), + Ratio::_192x => ratio._192x(), + Ratio::_256x => ratio._256x(), + Ratio::_384x => ratio._384x(), + Ratio::_512x => ratio._512x(), + } + }); + + c.swidth.write(|w| { + let swidth = w.swidth(); + match config.swidth { + SampleWidth::_8bit => swidth._8bit(), + SampleWidth::_16bit => swidth._16bit(), + SampleWidth::_24bit => swidth._24bit(), + } + }); + + c.align.write(|w| { + let align = w.align(); + match config.align { + Align::Left => align.left(), + Align::Right => align.right(), + } + }); + + c.format.write(|w| { + let format = w.format(); + match config.format { + Format::I2S => format.i2s(), + Format::Aligned => format.aligned(), + } + }); + + c.channels.write(|w| { + let channels = w.channels(); + match config.channels { + Channels::Stereo => channels.stereo(), + Channels::Left => channels.left(), + Channels::Right => channels.right(), + } + }); + } } impl<'d, T: Instance> I2sOutput<'d, T> { diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 60cde3b6..a395c714 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let config = i2s::Config::default(); - let mut i2s = i2s::I2s::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + let mut i2s = i2s::I2S::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); let len = signal_buf.0.len() / 2; From 5a64bf651c66f2da16cd3ae20ed9ba2489f40d7a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 10 Nov 2022 00:10:42 +0100 Subject: [PATCH 113/374] Buffer trait. Simpler config. --- embassy-nrf/src/i2s.rs | 122 +++++++++++++++++++----------------- examples/nrf/src/bin/i2s.rs | 7 +-- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 8752dfde..3f5491ee 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -284,7 +284,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] - pub fn stop(&self) -> &Self { + pub async fn stop(&self) -> &Self { todo!() } @@ -307,10 +307,12 @@ impl<'d, T: Instance> I2S<'d, T> { /// Transmits the given `tx_buffer`. /// Buffer address must be 4 byte aligned and located in RAM. /// Returns a value that represents the in-progress DMA transfer. - // TODO Define a better interface for the input buffer #[allow(unused_mut)] - pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { - self.output.tx(ptr, len).await + pub async fn tx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + self.output.tx(buffer).await } fn apply_config(c: &CONFIG, config: &Config) { @@ -319,54 +321,12 @@ impl<'d, T: Instance> I2S<'d, T> { c.mckfreq.write(|w| w.mckfreq()._32mdiv16()); c.mode.write(|w| w.mode().master()); - c.ratio.write(|w| { - let ratio = w.ratio(); - match config.ratio { - Ratio::_32x => ratio._32x(), - Ratio::_48x => ratio._48x(), - Ratio::_64x => ratio._64x(), - Ratio::_96x => ratio._96x(), - Ratio::_128x => ratio._128x(), - Ratio::_192x => ratio._192x(), - Ratio::_256x => ratio._256x(), - Ratio::_384x => ratio._384x(), - Ratio::_512x => ratio._512x(), - } - }); - - c.swidth.write(|w| { - let swidth = w.swidth(); - match config.swidth { - SampleWidth::_8bit => swidth._8bit(), - SampleWidth::_16bit => swidth._16bit(), - SampleWidth::_24bit => swidth._24bit(), - } - }); - - c.align.write(|w| { - let align = w.align(); - match config.align { - Align::Left => align.left(), - Align::Right => align.right(), - } - }); - - c.format.write(|w| { - let format = w.format(); - match config.format { - Format::I2S => format.i2s(), - Format::Aligned => format.aligned(), - } - }); - - c.channels.write(|w| { - let channels = w.channels(); - match config.channels { - Channels::Stereo => channels.stereo(), - Channels::Left => channels.left(), - Channels::Right => channels.right(), - } - }); + c.ratio.write(|w| unsafe { w.ratio().bits(config.ratio.into()) }); + c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) }); + c.align.write(|w| w.align().bit(config.align.into())); + c.format.write(|w| w.format().bit(config.format.into())); + c.channels + .write(|w| unsafe { w.channels().bits(config.channels.into()) }); } } @@ -374,18 +334,23 @@ impl<'d, T: Instance> I2sOutput<'d, T> { /// Transmits the given `tx_buffer`. /// Buffer address must be 4 byte aligned and located in RAM. /// Returns a value that represents the in-progress DMA transfer. - // TODO Define a better interface for the input buffer - pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { + pub async fn tx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let ptr = buffer.bytes_ptr(); + let len = buffer.bytes_len(); + if ptr as u32 % 4 != 0 { return Err(Error::BufferMisaligned); } - let maxcnt = (len / (core::mem::size_of::() / core::mem::size_of::())) as u32; - if maxcnt > MAX_DMA_MAXCNT { - return Err(Error::BufferTooLong); - } if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { return Err(Error::DMABufferNotInDataMemory); } + let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + if maxcnt > MAX_DMA_MAXCNT { + return Err(Error::BufferTooLong); + } let r = T::regs(); let _s = T::state(); @@ -401,6 +366,47 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } } +pub trait Buffer: Sized { + fn bytes_ptr(&self) -> *const u8; + fn bytes_len(&self) -> usize; +} + +impl Buffer for &[u8] { + #[inline] + fn bytes_ptr(&self) -> *const u8 { + self.as_ptr() + } + + #[inline] + fn bytes_len(&self) -> usize { + self.len() + } +} + +impl Buffer for &[i16] { + #[inline] + fn bytes_ptr(&self) -> *const u8 { + self.as_ptr() as *const u8 + } + + #[inline] + fn bytes_len(&self) -> usize { + self.len() * core::mem::size_of::() + } +} + +impl Buffer for &[i32] { + #[inline] + fn bytes_ptr(&self) -> *const u8 { + self.as_ptr() as *const u8 + } + + #[inline] + fn bytes_len(&self) -> usize { + self.len() * core::mem::size_of::() + } +} + pub(crate) mod sealed { use core::sync::atomic::AtomicU8; diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index a395c714..e8ddb4a4 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -26,14 +26,11 @@ async fn main(_spawner: Spawner) { signal_buf.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; } - let ptr = &signal_buf.0 as *const i16 as *const u8; - let len = signal_buf.0.len() * core::mem::size_of::(); - - i2s.start(); i2s.set_tx_enabled(true); + i2s.start(); loop { - match i2s.tx(ptr, len).await { + match i2s.tx(signal_buf.0.as_slice()).await { Ok(_) => todo!(), Err(_) => todo!(), }; From f22f36f51ba4466dd15df78df0ad86ac96f9051c Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 10 Nov 2022 00:24:49 +0100 Subject: [PATCH 114/374] Add input rx --- embassy-nrf/src/i2s.rs | 59 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 3f5491ee..fb6fa4bd 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -166,7 +166,7 @@ pub enum Mode { /// For more details about EasyDMA, consult the module documentation. pub struct I2S<'d, T: Instance> { output: I2sOutput<'d, T>, - _input: I2sInput<'d, T>, + input: I2sInput<'d, T>, } /// Transmitter interface to the UARTE peripheral obtained @@ -253,7 +253,7 @@ impl<'d, T: Instance> I2S<'d, T> { output: I2sOutput { _p: unsafe { i2s.clone_unchecked() }, }, - _input: I2sInput { _p: i2s }, + input: I2sInput { _p: i2s }, } } @@ -284,7 +284,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] - pub async fn stop(&self) -> &Self { + pub async fn stop(&self) { todo!() } @@ -304,10 +304,8 @@ impl<'d, T: Instance> I2S<'d, T> { self } - /// Transmits the given `tx_buffer`. + /// Transmits the given `buffer`. /// Buffer address must be 4 byte aligned and located in RAM. - /// Returns a value that represents the in-progress DMA transfer. - #[allow(unused_mut)] pub async fn tx(&mut self, buffer: B) -> Result<(), Error> where B: Buffer, @@ -315,6 +313,15 @@ impl<'d, T: Instance> I2S<'d, T> { self.output.tx(buffer).await } + /// Receives data into the given `buffer` until it's filled. + /// Buffer address must be 4 byte aligned and located in RAM. + pub async fn rx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + self.input.rx(buffer).await + } + fn apply_config(c: &CONFIG, config: &Config) { // TODO support slave too c.mcken.write(|w| w.mcken().enabled()); @@ -331,9 +338,9 @@ impl<'d, T: Instance> I2S<'d, T> { } impl<'d, T: Instance> I2sOutput<'d, T> { - /// Transmits the given `tx_buffer`. + /// Transmits the given `buffer`. /// Buffer address must be 4 byte aligned and located in RAM. - /// Returns a value that represents the in-progress DMA transfer. + #[allow(unused_mut)] pub async fn tx(&mut self, buffer: B) -> Result<(), Error> where B: Buffer, @@ -366,6 +373,42 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } } +impl<'d, T: Instance> I2sInput<'d, T> { + /// Receives into the given `buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + #[allow(unused_mut)] + pub async fn rx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let ptr = buffer.bytes_ptr(); + let len = buffer.bytes_len(); + + if ptr as u32 % 4 != 0 { + return Err(Error::BufferMisaligned); + } + if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + return Err(Error::DMABufferNotInDataMemory); + } + let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + if maxcnt > MAX_DMA_MAXCNT { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + let _s = T::state(); + + // TODO we can not progress until the last buffer written in RXD.PTR + // has started the transmission. + // We can use some sync primitive from `embassy-sync`. + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + + Ok(()) + } +} + pub trait Buffer: Sized { fn bytes_ptr(&self) -> *const u8; fn bytes_len(&self) -> usize; From dbe97b409891ed865d07edc29cec4d8af13e55d9 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 10 Nov 2022 14:37:42 +0100 Subject: [PATCH 115/374] Adapted nvmc so it can be used for all nrf targets --- embassy-nrf/src/chips/nrf5340_app.rs | 5 ++ embassy-nrf/src/chips/nrf5340_net.rs | 5 ++ embassy-nrf/src/chips/nrf9160.rs | 5 ++ embassy-nrf/src/lib.rs | 1 - embassy-nrf/src/nvmc.rs | 70 ++++++++++++++++++++++++---- 5 files changed, 75 insertions(+), 11 deletions(-) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 7845d4a8..e20edcdf 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -213,6 +213,8 @@ pub mod pac { pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FLASH_SIZE: usize = 1024 * 1024; + embassy_hal_common::peripherals! { // USB USBD, @@ -224,6 +226,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // UARTE, TWI & SPI UARTETWISPI0, UARTETWISPI1, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index ae136e09..8c292e52 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -104,6 +104,8 @@ pub mod pac { pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FLASH_SIZE: usize = 256 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -112,6 +114,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // UARTE, TWI & SPI UARTETWISPI0, UARTETWISPI1, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index b5a53ed8..5c00b65a 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -164,6 +164,8 @@ pub mod pac { pub const EASY_DMA_SIZE: usize = (1 << 13) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FLASH_SIZE: usize = 1024 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -172,6 +174,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // UARTE, TWI & SPI UARTETWISPI0, UARTETWISPI1, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f..cac55071 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,7 +74,6 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; #[cfg(any( feature = "nrf52810", diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 6f66f7a7..ba6a5912 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -10,8 +10,12 @@ use embedded_storage::nor_flash::{ use crate::peripherals::NVMC; use crate::{pac, Peripheral}; +#[cfg(not(feature = "_nrf5340-net"))] /// Erase size of NVMC flash in bytes. pub const PAGE_SIZE: usize = 4096; +#[cfg(feature = "_nrf5340-net")] +/// Erase size of NVMC flash in bytes. +pub const PAGE_SIZE: usize = 2048; /// Size of NVMC flash in bytes. pub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE; @@ -55,6 +59,56 @@ impl<'d> Nvmc<'d> { let p = Self::regs(); while p.ready.read().ready().is_busy() {} } + + #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] + fn wait_ready_write(&mut self) { + self.wait_ready(); + } + + #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] + fn wait_ready_write(&mut self) { + let p = Self::regs(); + while p.readynext.read().readynext().is_busy() {} + } + + #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] + fn erase_page(&mut self, page: u32) { + Self::regs().erasepage().write(|w| unsafe { w.bits(page) }); + } + + #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] + fn erase_page(&mut self, page: u32) { + #[cfg(not(feature = "_nrf5340-net"))] + const FLASH_START_ADDR: u32 = 0; + #[cfg(feature = "_nrf5340-net")] + const FLASH_START_ADDR: u32 = 0x100_0000; + + let first_page_word = (FLASH_START_ADDR + page * PAGE_SIZE as u32) as *mut u32; + unsafe { + first_page_word.write_volatile(0xFFFF_FFFF); + } + } + + fn enable_erase(&self) { + #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + Self::regs().config.write(|w| w.wen().een()); + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + Self::regs().configns.write(|w| w.wen().een()); + } + + fn enable_read(&self) { + #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + Self::regs().config.write(|w| w.wen().ren()); + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + Self::regs().configns.write(|w| w.wen().ren()); + } + + fn enable_write(&self) { + #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + Self::regs().config.write(|w| w.wen().wen()); + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + Self::regs().configns.write(|w| w.wen().wen()); + } } impl<'d> MultiwriteNorFlash for Nvmc<'d> {} @@ -93,17 +147,15 @@ impl<'d> NorFlash for Nvmc<'d> { return Err(Error::Unaligned); } - let p = Self::regs(); - - p.config.write(|w| w.wen().een()); + self.enable_erase(); self.wait_ready(); for page in (from..to).step_by(PAGE_SIZE) { - p.erasepage().write(|w| unsafe { w.bits(page) }); + self.erase_page(page); self.wait_ready(); } - p.config.reset(); + self.enable_read(); self.wait_ready(); Ok(()) @@ -117,9 +169,7 @@ impl<'d> NorFlash for Nvmc<'d> { return Err(Error::Unaligned); } - let p = Self::regs(); - - p.config.write(|w| w.wen().wen()); + self.enable_write(); self.wait_ready(); unsafe { @@ -129,11 +179,11 @@ impl<'d> NorFlash for Nvmc<'d> { for i in 0..words { let w = ptr::read_unaligned(p_src.add(i)); ptr::write_volatile(p_dst.add(i), w); - self.wait_ready(); + self.wait_ready_write(); } } - p.config.reset(); + self.enable_read(); self.wait_ready(); Ok(()) From cbc97758e391d981d497bf37fe63b5a35913c115 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 9 Nov 2022 14:10:46 +0100 Subject: [PATCH 116/374] stm32: Fix watchdog division by zero for 256 prescaler, add watchdog example for H7 --- embassy-stm32/src/wdg/mod.rs | 8 ++++---- examples/stm32h7/src/bin/wdg.rs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 examples/stm32h7/src/bin/wdg.rs diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 85176eef..92b9a5ca 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -13,12 +13,12 @@ pub struct IndependentWatchdog<'d, T: Instance> { const MAX_RL: u16 = 0xFFF; /// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler -const fn max_timeout(prescaler: u8) -> u32 { +const fn max_timeout(prescaler: u16) -> u32 { 1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32) } /// Calculates watchdog reload value for the given prescaler and desired timeout -const fn reload_value(prescaler: u8, timeout_us: u32) -> u16 { +const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 { (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 } @@ -33,12 +33,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { // Find lowest prescaler value, which makes watchdog period longer or equal to timeout. // This iterates from 4 (2^2) to 256 (2^8). let psc_power = unwrap!((2..=8).find(|psc_power| { - let psc = 2u8.pow(*psc_power); + let psc = 2u16.pow(*psc_power); timeout_us <= max_timeout(psc) })); // Prescaler value - let psc = 2u8.pow(psc_power); + let psc = 2u16.pow(psc_power); // Convert prescaler power to PR register value let pr = psc_power as u8 - 2; diff --git a/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs new file mode 100644 index 00000000..2b0301aa --- /dev/null +++ b/examples/stm32h7/src/bin/wdg.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::wdg::IndependentWatchdog; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000); + + unsafe { wdg.unleash() }; + + loop { + Timer::after(Duration::from_secs(1)).await; + unsafe { wdg.pet() }; + } +} From 99682d313b54409c33d471e066ef6aba9f628a68 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 25 Oct 2022 09:47:30 +0200 Subject: [PATCH 117/374] Disable MMC interrupts MMC interrupts can cause firmware hangup - refer to: https://github.com/stm32-rs/stm32h7xx-hal/issues/275 for more information --- embassy-stm32/src/eth/v2/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index a81ee118..5b76d1e7 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -116,6 +116,24 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); + // disable all MMC RX interrupts + mac.mmc_rx_interrupt_mask().write(|w| { + w.set_rxcrcerpim(true); + w.set_rxalgnerpim(true); + w.set_rxucgpim(true); + w.set_rxlpiuscim(true); + w.set_rxlpitrcim(true) + }); + + // disable all MMC TX interrupts + mac.mmc_tx_interrupt_mask().write(|w| { + w.set_txscolgpim(true); + w.set_txmcolgpim(true); + w.set_txgpktim(true); + w.set_txlpiuscim(true); + w.set_txlpitrcim(true); + }); + mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); From 6e1120e17e384a04cd3aef58a1467a4a0a862ba5 Mon Sep 17 00:00:00 2001 From: Sijmen Woutersen Date: Sun, 25 Sep 2022 20:10:11 +0200 Subject: [PATCH 118/374] riscv support --- embassy-executor/Cargo.toml | 1 + embassy-executor/src/arch/riscv32.rs | 15 +-------------- embassy-macros/Cargo.toml | 1 + embassy-macros/src/macros/main.rs | 15 +++++++++++++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d0f51646..e6b4c596 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -25,6 +25,7 @@ flavors = [ default = [] std = ["embassy-macros/std", "critical-section/std"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"] +riscv = ["embassy-macros/riscv"] # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 2a4b006d..e095c0ee 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -54,20 +54,7 @@ impl Executor { loop { unsafe { self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here + core::arch::asm!("wfi"); } } } diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 91d5ec8a..c5ed3b5d 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true [features] std = [] wasm = [] +riscv = [] # Enabling this cause interrupt::take! to require embassy-executor rtos-trace-interrupt = [] diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index afe9bd3e..54806847 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -45,7 +45,7 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result ! { let mut executor = ::embassy_executor::Executor::new(); @@ -57,13 +57,24 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result ! { let mut executor = ::embassy_executor::Executor::new(); let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + }; + #[cfg(all(not(feature = "std"), not(feature = "wasm"), feature = "riscv"))] + let main = quote! { + #[riscv_rt::entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; executor.run(|spawner| { spawner.must_spawn(__embassy_main(spawner)); }) From 4a2e810485a996014999ad630a604c3fe4fc81a4 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 10 Nov 2022 23:13:01 +0100 Subject: [PATCH 119/374] Restrict to pacs supporting i2s --- embassy-nrf/Cargo.toml | 1 - embassy-nrf/src/lib.rs | 6 +++++- examples/nrf/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index aa1576fd..67b6bec4 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -48,7 +48,6 @@ nrf9160-s = ["_nrf9160"] nrf9160-ns = ["_nrf9160"] gpiote = [] -i2s = [] time-driver-rtc1 = ["_time-driver"] # Features starting with `_` are for internal use only. They're not intended diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ac797db9..95bd5831 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,7 +74,11 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -// #[cfg(all(feature = "i2s", feature = "nrf52840"))] +#[cfg(any( + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", +))] pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a79044e8..c633f82f 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "i2s", "unstable-pac"] } +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 } embedded-io = "0.3.1" From 10e3c3f2ec358da6d81f2bb9c05936c2ab6da567 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 11 Nov 2022 23:49:20 +0100 Subject: [PATCH 120/374] Cargo fmt --- embassy-nrf/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 95bd5831..bdb911ec 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,11 +74,7 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(any( - feature = "nrf52832", - feature = "nrf52833", - feature = "nrf52840", -))] +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840",))] pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; From e70ae71eccf370c42043fa323002c14f94a16679 Mon Sep 17 00:00:00 2001 From: Sijmen Woutersen Date: Sat, 12 Nov 2022 10:56:49 +0100 Subject: [PATCH 121/374] restore SIGNAL_WORK_THREAD_MODE --- embassy-executor/src/arch/riscv32.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index e095c0ee..76eb8b11 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -54,7 +54,12 @@ impl Executor { loop { unsafe { self.inner.poll(); - core::arch::asm!("wfi"); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + if !SIGNAL_WORK_THREAD_MODE.fetch_and(false, Ordering::SeqCst) { + core::arch::asm!("wfi"); + } } } } From 122a31d20877005c7201d4e7c98da5544666dd1d Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 12 Nov 2022 18:48:57 +0100 Subject: [PATCH 122/374] Interrupts, async, sine oscillator --- embassy-nrf/src/i2s.rs | 298 ++++++++++++++++++++++++++---------- embassy-nrf/src/lib.rs | 2 +- examples/nrf/src/bin/i2s.rs | 132 +++++++++++++--- 3 files changed, 330 insertions(+), 102 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index fb6fa4bd..f5e36f0d 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -2,17 +2,18 @@ //! I2S -//use core::future::poll_fn; -//use core::sync::atomic::{compiler_fence, Ordering}; -//use core::task::Poll; +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; -//use embassy_hal_common::drop::OnDrop; +use embassy_cortex_m::interrupt::{InterruptExt, Priority}; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; //use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; -use crate::pac::i2s::CONFIG; +use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL}; use crate::Peripheral; // TODO: Define those in lib.rs somewhere else @@ -35,10 +36,39 @@ pub enum Error { // TODO: add other error variants. } +pub const MODE_MASTER_8000: Mode = Mode::Master { + freq: MckFreq::_32MDiv125, + ratio: Ratio::_32x, +}; // error = 0 +pub const MODE_MASTER_11025: Mode = Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_192x, +}; // error = 86 +pub const MODE_MASTER_16000: Mode = Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_96x, +}; // error = 127 +pub const MODE_MASTER_22050: Mode = Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_96x, +}; // error = 172 +pub const MODE_MASTER_32000: Mode = Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_48x, +}; // error = 254 +pub const MODE_MASTER_44100: Mode = Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_48x, +}; // error = 344 +pub const MODE_MASTER_48000: Mode = Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_32x, +}; // error = 381 + #[derive(Clone)] #[non_exhaustive] pub struct Config { - pub ratio: Ratio, + pub mode: Mode, pub swidth: SampleWidth, pub align: Align, pub format: Format, @@ -48,7 +78,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - ratio: Ratio::_32x, + mode: MODE_MASTER_32000, swidth: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, @@ -57,6 +87,66 @@ impl Default for Config { } } +/// I2S Mode +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Mode { + Master { freq: MckFreq, ratio: Ratio }, + Slave, +} + +impl Mode { + pub fn sample_rate(&self) -> Option { + match self { + Mode::Master { freq, ratio } => Some(freq.to_frequency() / ratio.to_divisor()), + Mode::Slave => None, + } + } +} + +/// Master clock generator frequency. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum MckFreq { + _32MDiv8, + _32MDiv10, + _32MDiv11, + _32MDiv15, + _32MDiv16, + _32MDiv21, + _32MDiv23, + _32MDiv30, + _32MDiv31, + _32MDiv32, + _32MDiv42, + _32MDiv63, + _32MDiv125, +} + +impl MckFreq { + const REGISTER_VALUES: &[u32] = &[ + 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, + 0x08000000, 0x06000000, 0x04100000, 0x020C0000, + ]; + + const FREQUENCIES: &[u32] = &[ + 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, + 256000, + ]; + + pub fn to_register_value(&self) -> u32 { + Self::REGISTER_VALUES[usize::from(*self)] + } + + pub fn to_frequency(&self) -> u32 { + Self::FREQUENCIES[usize::from(*self)] + } +} + +impl From for usize { + fn from(variant: MckFreq) -> Self { + variant as _ + } +} + /// MCK / LRCK ratio. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Ratio { @@ -71,6 +161,14 @@ pub enum Ratio { _512x, } +impl Ratio { + const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; + + pub fn to_divisor(&self) -> u32 { + Self::RATIOS[u8::from(*self) as usize] + } +} + impl From for u8 { fn from(variant: Ratio) -> Self { variant as _ @@ -136,31 +234,6 @@ impl From for u8 { } } -/// I2S Mode -#[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum Mode { - Controller, - Peripheral, -} - -// /// Master clock generator frequency. -// #[derive(Debug, Eq, PartialEq, Clone, Copy)] -// pub enum MckFreq { -// _32MDiv8 = 0x20000000, -// _32MDiv10 = 0x18000000, -// _32MDiv11 = 0x16000000, -// _32MDiv15 = 0x11000000, -// _32MDiv16 = 0x10000000, -// _32MDiv21 = 0x0C000000, -// _32MDiv23 = 0x0B000000, -// _32MDiv30 = 0x08800000, -// _32MDiv31 = 0x08400000, -// _32MDiv32 = 0x08000000, -// _32MDiv42 = 0x06000000, -// _32MDiv63 = 0x04100000, -// _32MDiv125 = 0x020C0000, -// } - /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. @@ -185,7 +258,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S pub fn new( i2s: impl Peripheral

+ 'd, - // irq: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, mck: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, @@ -196,7 +269,7 @@ impl<'d, T: Instance> I2S<'d, T> { into_ref!(mck, sck, lrck, sdin, sdout); Self::new_inner( i2s, - // irq, + irq, mck.map_into(), sck.map_into(), lrck.map_into(), @@ -208,7 +281,7 @@ impl<'d, T: Instance> I2S<'d, T> { fn new_inner( i2s: impl Peripheral

+ 'd, - // irq: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, mck: PeripheralRef<'d, AnyPin>, sck: PeripheralRef<'d, AnyPin>, lrck: PeripheralRef<'d, AnyPin>, @@ -216,36 +289,12 @@ impl<'d, T: Instance> I2S<'d, T> { sdout: PeripheralRef<'d, AnyPin>, config: Config, ) -> Self { - into_ref!(i2s, /* irq, */ mck, sck, lrck, sdin, sdout); + into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout); let r = T::regs(); - Self::apply_config(&r.config, &config); - - r.psel.mck.write(|w| { - unsafe { w.bits(mck.psel_bits()) }; - w.connect().connected() - }); - - r.psel.sck.write(|w| { - unsafe { w.bits(sck.psel_bits()) }; - w.connect().connected() - }); - - r.psel.lrck.write(|w| { - unsafe { w.bits(lrck.psel_bits()) }; - w.connect().connected() - }); - - r.psel.sdin.write(|w| { - unsafe { w.bits(sdin.psel_bits()) }; - w.connect().connected() - }); - - r.psel.sdout.write(|w| { - unsafe { w.bits(sdout.psel_bits()) }; - w.connect().connected() - }); + Self::select_pins(&r.psel, mck, sck, lrck, sdin, sdout); + Self::setup_interrupt(irq, r); r.enable.write(|w| w.enable().enabled()); @@ -322,19 +371,87 @@ impl<'d, T: Instance> I2S<'d, T> { self.input.rx(buffer).await } - fn apply_config(c: &CONFIG, config: &Config) { - // TODO support slave too - c.mcken.write(|w| w.mcken().enabled()); - c.mckfreq.write(|w| w.mckfreq()._32mdiv16()); - c.mode.write(|w| w.mode().master()); + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_txptrupd.read().bits() != 0 { + s.tx_waker.wake(); + r.intenclr.write(|w| w.txptrupd().clear()); + } + + if r.events_rxptrupd.read().bits() != 0 { + s.rx_waker.wake(); + r.intenclr.write(|w| w.rxptrupd().clear()); + } + } + + fn apply_config(c: &CONFIG, config: &Config) { + match config.mode { + Mode::Master { freq, ratio } => { + c.mode.write(|w| w.mode().master()); + c.mcken.write(|w| w.mcken().enabled()); + c.mckfreq + .write(|w| unsafe { w.mckfreq().bits(freq.to_register_value()) }); + c.ratio.write(|w| unsafe { w.ratio().bits(ratio.into()) }); + } + Mode::Slave => { + c.mode.write(|w| w.mode().slave()); + } + }; - c.ratio.write(|w| unsafe { w.ratio().bits(config.ratio.into()) }); c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) }); c.align.write(|w| w.align().bit(config.align.into())); c.format.write(|w| w.format().bit(config.format.into())); c.channels .write(|w| unsafe { w.channels().bits(config.channels.into()) }); } + + fn select_pins( + psel: &PSEL, + mck: PeripheralRef<'d, AnyPin>, + sck: PeripheralRef<'d, AnyPin>, + lrck: PeripheralRef<'d, AnyPin>, + sdin: PeripheralRef<'d, AnyPin>, + sdout: PeripheralRef<'d, AnyPin>, + ) { + psel.mck.write(|w| { + unsafe { w.bits(mck.psel_bits()) }; + w.connect().connected() + }); + + psel.sck.write(|w| { + unsafe { w.bits(sck.psel_bits()) }; + w.connect().connected() + }); + + psel.lrck.write(|w| { + unsafe { w.bits(lrck.psel_bits()) }; + w.connect().connected() + }); + + psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + + psel.sdout.write(|w| { + unsafe { w.bits(sdout.psel_bits()) }; + w.connect().connected() + }); + } + + fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) { + irq.set_handler(Self::on_interrupt); + irq.set_priority(Priority::P1); // TODO review priorities + irq.unpend(); + irq.enable(); + + r.intenclr.write(|w| w.rxptrupd().clear()); + r.intenclr.write(|w| w.txptrupd().clear()); + r.events_rxptrupd.reset(); + r.events_txptrupd.reset(); + } } impl<'d, T: Instance> I2sOutput<'d, T> { @@ -360,15 +477,40 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } let r = T::regs(); - let _s = T::state(); + let s = T::state(); - // TODO we can not progress until the last buffer written in TXD.PTR - // has started the transmission. - // We can use some sync primitive from `embassy-sync`. + let drop = OnDrop::new(move || { + trace!("write drop: stopping"); + + r.intenclr.write(|w| w.txptrupd().clear()); + r.events_txptrupd.reset(); + r.config.txen.write(|w| w.txen().disabled()); + + // TX is stopped almost instantly, spinning is fine. + while r.events_txptrupd.read().bits() == 0 {} + trace!("write drop: stopped"); + }); r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + r.intenset.write(|w| w.txptrupd().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + s.tx_waker.register(cx.waker()); + if r.events_txptrupd.read().bits() != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + compiler_fence(Ordering::SeqCst); + drop.defuse(); + Ok(()) } } @@ -451,23 +593,19 @@ impl Buffer for &[i32] { } pub(crate) mod sealed { - use core::sync::atomic::AtomicU8; - use embassy_sync::waitqueue::AtomicWaker; //use super::*; pub struct State { - pub input_waker: AtomicWaker, - pub output_waker: AtomicWaker, - pub buffers_refcount: AtomicU8, + pub rx_waker: AtomicWaker, + pub tx_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - input_waker: AtomicWaker::new(), - output_waker: AtomicWaker::new(), - buffers_refcount: AtomicU8::new(0), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), } } } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bdb911ec..dc018e08 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,7 +74,7 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840",))] +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index e8ddb4a4..53ccb3b8 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -4,43 +4,133 @@ #![no_main] #![feature(type_alias_impl_trait)] -//use defmt::*; +use core::f32::consts::PI; + +use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s; +use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000}; +use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[repr(align(4))] -pub struct Aligned(T); +pub struct AlignedBuffer(T); + +impl AsRef for AlignedBuffer { + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl AsMut for AlignedBuffer { + fn as_mut(&mut self) -> &mut T { + &mut self.0 + } +} #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let config = i2s::Config::default(); + let mut config = i2s::Config::default(); + // config.mode = MODE_MASTER_16000; + config.mode = Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_256x, + }; // 12500 Hz + let sample_rate = config.mode.sample_rate().expect("I2S Master"); + let inv_sample_rate = 1.0 / sample_rate as f32; - let mut i2s = i2s::I2S::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + info!("Sample rate: {}", sample_rate); - let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); - let len = signal_buf.0.len() / 2; - for x in 0..len { - signal_buf.0[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; - signal_buf.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; - } + let irq = interrupt::take!(I2S); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + + const BUF_SAMPLES: usize = 250; + const BUF_SIZE: usize = BUF_SAMPLES * 2; + let mut buf = AlignedBuffer([0i16; BUF_SIZE]); + + let mut carrier = SineOsc::new(); + carrier.set_frequency(300.0, inv_sample_rate); + + let mut modulator = SineOsc::new(); + modulator.set_frequency(0.01, inv_sample_rate); + modulator.set_amplitude(0.2); i2s.set_tx_enabled(true); i2s.start(); loop { - match i2s.tx(signal_buf.0.as_slice()).await { - Ok(_) => todo!(), - Err(_) => todo!(), - }; + for sample in buf.as_mut().chunks_mut(2) { + let signal = carrier.generate(); + // let modulation = bipolar_to_unipolar(modulator.generate()); + // carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); + // carrier.set_amplitude((modulation); + let value = (i16::MAX as f32 * signal) as i16; + sample[0] = value; + sample[1] = value; + // info!("{}", signal); + } + + if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { + error!("{}", err); + } } } -fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 { - let length = length as i32; - amplitude - - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) % (2 * amplitude) - amplitude) - .abs() - - amplitude / 2 +struct SineOsc { + amplitude: f32, + modulo: f32, + phase_inc: f32, +} + +impl SineOsc { + const B: f32 = 4.0 / PI; + const C: f32 = -4.0 / (PI * PI); + const P: f32 = 0.225; + + pub fn new() -> Self { + Self { + amplitude: 1.0, + modulo: 0.0, + phase_inc: 0.0, + } + } + + pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { + self.phase_inc = freq * inv_sample_rate; + } + + pub fn set_amplitude(&mut self, amplitude: f32) { + self.amplitude = amplitude; + } + + pub fn generate(&mut self) -> f32 { + let signal = self.parabolic_sin(self.modulo); + self.modulo += self.phase_inc; + if self.modulo < 0.0 { + self.modulo += 1.0; + } else if self.modulo > 1.0 { + self.modulo -= 1.0; + } + signal * self.amplitude + } + + fn parabolic_sin(&mut self, modulo: f32) -> f32 { + let angle = PI - modulo * 2.0 * PI; + let y = Self::B * angle + Self::C * angle * abs(angle); + Self::P * (y * abs(y) - y) + y + } +} + +#[inline] +fn abs(value: f32) -> f32 { + if value < 0.0 { + -value + } else { + value + } +} + +#[inline] +fn bipolar_to_unipolar(value: f32) -> f32 { + (value + 1.0) / 2.0 } From d2e8794f29d3d0afef7a6bc610b2ee4a4d680643 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sun, 13 Nov 2022 01:41:32 +0100 Subject: [PATCH 123/374] Investigating discontinuities in the signal --- embassy-nrf/src/i2s.rs | 68 ++++++++++++++++++++++++++----------- examples/nrf/src/bin/i2s.rs | 27 ++++++++++----- 2 files changed, 66 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index f5e36f0d..9a8f29e7 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -371,21 +371,6 @@ impl<'d, T: Instance> I2S<'d, T> { self.input.rx(buffer).await } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_txptrupd.read().bits() != 0 { - s.tx_waker.wake(); - r.intenclr.write(|w| w.txptrupd().clear()); - } - - if r.events_rxptrupd.read().bits() != 0 { - s.rx_waker.wake(); - r.intenclr.write(|w| w.rxptrupd().clear()); - } - } - fn apply_config(c: &CONFIG, config: &Config) { match config.mode { Mode::Master { freq, ratio } => { @@ -443,14 +428,36 @@ impl<'d, T: Instance> I2S<'d, T> { fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) { irq.set_handler(Self::on_interrupt); - irq.set_priority(Priority::P1); // TODO review priorities + // irq.set_priority(Priority::P1); // TODO review priorities irq.unpend(); irq.enable(); r.intenclr.write(|w| w.rxptrupd().clear()); r.intenclr.write(|w| w.txptrupd().clear()); + r.events_rxptrupd.reset(); r.events_txptrupd.reset(); + + r.intenset.write(|w| w.rxptrupd().set()); + r.intenset.write(|w| w.txptrupd().set()); + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_txptrupd.read().bits() != 0 { + trace!("[{}] INT", s.seq.load(Ordering::Relaxed)); + s.tx_waker.wake(); + r.intenclr.write(|w| w.txptrupd().clear()); + } + + if r.events_rxptrupd.read().bits() != 0 { + s.rx_waker.wake(); + r.intenclr.write(|w| w.rxptrupd().clear()); + } + + s.overruns.fetch_add(1, Ordering::Relaxed); } } @@ -479,6 +486,12 @@ impl<'d, T: Instance> I2sOutput<'d, T> { let r = T::regs(); let s = T::state(); + let seq = s.seq.fetch_add(1, Ordering::Relaxed); + if r.events_txptrupd.read().bits() != 0 && seq > 0 { + info!("XRUN!"); + loop {} + } + let drop = OnDrop::new(move || { trace!("write drop: stopping"); @@ -491,18 +504,26 @@ impl<'d, T: Instance> I2sOutput<'d, T> { trace!("write drop: stopped"); }); + trace!("[{}] PTR", s.seq.load(Ordering::Relaxed)); r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - r.intenset.write(|w| w.txptrupd().set()); - compiler_fence(Ordering::SeqCst); poll_fn(|cx| { s.tx_waker.register(cx.waker()); - if r.events_txptrupd.read().bits() != 0 { + if r.events_txptrupd.read().bits() != 0 || seq == 0 { + trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed)); + r.events_txptrupd.reset(); + r.intenset.write(|w| w.txptrupd().set()); + let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); + if overruns - 1 != 0 { + warn!("XRUN: {}", overruns); + s.overruns.store(0, Ordering::Relaxed) + } Poll::Ready(()) } else { + trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed)); Poll::Pending } }) @@ -593,19 +614,26 @@ impl Buffer for &[i32] { } pub(crate) mod sealed { + use core::sync::atomic::AtomicI32; + use embassy_sync::waitqueue::AtomicWaker; - //use super::*; + use super::*; pub struct State { pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, + pub overruns: AtomicI32, + pub seq: AtomicI32, } + impl State { pub const fn new() -> Self { Self { rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), + overruns: AtomicI32::new(0), + seq: AtomicI32::new(0), } } } diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 53ccb3b8..7fb1ecb8 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -9,6 +9,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000}; +use embassy_nrf::pac::ficr::info; use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; @@ -49,16 +50,14 @@ async fn main(_spawner: Spawner) { let mut buf = AlignedBuffer([0i16; BUF_SIZE]); let mut carrier = SineOsc::new(); - carrier.set_frequency(300.0, inv_sample_rate); + carrier.set_frequency(240.0, inv_sample_rate); - let mut modulator = SineOsc::new(); - modulator.set_frequency(0.01, inv_sample_rate); - modulator.set_amplitude(0.2); + // let mut modulator = SineOsc::new(); + // modulator.set_frequency(0.01, inv_sample_rate); + // modulator.set_amplitude(0.2); - i2s.set_tx_enabled(true); - i2s.start(); - - loop { + let mut lastf = 0.0; + let mut generate = |buf: &mut [i16]| { for sample in buf.as_mut().chunks_mut(2) { let signal = carrier.generate(); // let modulation = bipolar_to_unipolar(modulator.generate()); @@ -67,8 +66,18 @@ async fn main(_spawner: Spawner) { let value = (i16::MAX as f32 * signal) as i16; sample[0] = value; sample[1] = value; - // info!("{}", signal); } + }; + + generate(buf.as_mut().as_mut_slice()); + + i2s.set_tx_enabled(true); + i2s.start(); + + loop { + // info!("--"); + + generate(buf.as_mut().as_mut_slice()); if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { error!("{}", err); From dca11095e2f41d50dbc96d48474a64d9198728c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Sun, 13 Nov 2022 01:49:55 +0100 Subject: [PATCH 124/374] Disable UARTE in embassy-nrf::init --- embassy-nrf/src/lib.rs | 6 ++++++ embassy-nrf/src/spis.rs | 39 +++++++++++------------------------- examples/nrf/src/bin/spis.rs | 8 +++++--- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 587e19be..67b25498 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -268,5 +268,11 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); + // Disable UARTE (enabled by default for some reason) + unsafe { + (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); + (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); + } + peripherals } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 3f77c61d..416db2d3 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -1,5 +1,4 @@ #![macro_use] - use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -10,7 +9,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; +use crate::gpio::{self, AnyPin, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -122,41 +121,26 @@ impl<'d, T: Instance> Spis<'d, T> { mosi: Option>, config: Config, ) -> Self { - into_ref!(cs, spis, irq); + compiler_fence(Ordering::SeqCst); + + into_ref!(spis, irq, cs, sck); let r = T::regs(); // Configure pins. sck.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); cs.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); if let Some(mosi) = &mosi { mosi.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); } if let Some(miso) = &miso { miso.conf().write(|w| w.dir().output().drive().h0h1()); + r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); } - match config.mode.polarity { - Polarity::IdleHigh => { - sck.set_high(); - if let Some(mosi) = &mosi { - mosi.set_high(); - } - } - Polarity::IdleLow => { - sck.set_low(); - if let Some(mosi) = &mosi { - mosi.set_low(); - } - } - } - - // Select pins. - r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); - r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); - r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); - r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); - // Enable SPIS instance. r.enable.write(|w| w.enable().enabled()); @@ -246,9 +230,8 @@ impl<'d, T: Instance> Spis<'d, T> { r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - // Reset and enable the end event. + // Reset end event. r.events_end.reset(); - r.intenset.write(|w| w.end().set()); // Release the semaphore. r.tasks_release.write(|w| unsafe { w.bits(1) }); @@ -316,6 +299,7 @@ impl<'d, T: Instance> Spis<'d, T> { if r.semstat.read().bits() == 1 { return Poll::Ready(()); } + r.intenset.write(|w| w.acquired().set()); Poll::Pending }) .await; @@ -324,12 +308,13 @@ impl<'d, T: Instance> Spis<'d, T> { self.prepare(rx, tx)?; // Wait for 'end' event. + r.intenset.write(|w| w.end().set()); poll_fn(|cx| { s.end_waker.register(cx.waker()); if r.events_end.read().bits() != 0 { return Poll::Ready(()); } - + r.intenset.write(|w| w.end().set()); Poll::Pending }) .await; diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs index dade5fcb..fe3b0c53 100644 --- a/examples/nrf/src/bin/spis.rs +++ b/examples/nrf/src/bin/spis.rs @@ -17,9 +17,11 @@ async fn main(_spawner: Spawner) { let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); loop { - let mut buf = [0_u8; 64]; - if let Ok(n) = spis.read(&mut buf).await { - info!("RX: {:?}", buf[..n]); + let mut rx_buf = [0_u8; 64]; + let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8]; + if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await { + info!("RX: {:?}", rx_buf[..n_rx]); + info!("TX: {:?}", tx_buf[..n_tx]); } } } From 17857bc18fee95be07ee0c51687d2eb109e5aea6 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sun, 13 Nov 2022 02:12:58 +0100 Subject: [PATCH 125/374] Minor changes --- embassy-nrf/src/i2s.rs | 9 +++++---- examples/nrf/src/bin/i2s.rs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 9a8f29e7..eed9e195 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -327,6 +327,7 @@ impl<'d, T: Instance> I2S<'d, T> { pub fn start(&self) -> &Self { let r = T::regs(); self.enable(); + trace!("START"); r.tasks_start.write(|w| unsafe { w.bits(1) }); self } @@ -487,8 +488,8 @@ impl<'d, T: Instance> I2sOutput<'d, T> { let s = T::state(); let seq = s.seq.fetch_add(1, Ordering::Relaxed); - if r.events_txptrupd.read().bits() != 0 && seq > 0 { - info!("XRUN!"); + if r.events_txptrupd.read().bits() != 0 && seq > 1 { + warn!("XRUN!"); loop {} } @@ -505,8 +506,8 @@ impl<'d, T: Instance> I2sOutput<'d, T> { }); trace!("[{}] PTR", s.seq.load(Ordering::Relaxed)); - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); compiler_fence(Ordering::SeqCst); @@ -517,7 +518,7 @@ impl<'d, T: Instance> I2sOutput<'d, T> { r.events_txptrupd.reset(); r.intenset.write(|w| w.txptrupd().set()); let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); - if overruns - 1 != 0 { + if overruns != 0 { warn!("XRUN: {}", overruns); s.overruns.store(0, Ordering::Relaxed) } diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 7fb1ecb8..33b5398d 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(I2S); let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); - const BUF_SAMPLES: usize = 250; + const BUF_SAMPLES: usize = 500; const BUF_SIZE: usize = BUF_SAMPLES * 2; let mut buf = AlignedBuffer([0i16; BUF_SIZE]); @@ -56,7 +56,6 @@ async fn main(_spawner: Spawner) { // modulator.set_frequency(0.01, inv_sample_rate); // modulator.set_amplitude(0.2); - let mut lastf = 0.0; let mut generate = |buf: &mut [i16]| { for sample in buf.as_mut().chunks_mut(2) { let signal = carrier.generate(); @@ -71,12 +70,14 @@ async fn main(_spawner: Spawner) { generate(buf.as_mut().as_mut_slice()); + if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { + error!("{}", err); + } + i2s.set_tx_enabled(true); i2s.start(); loop { - // info!("--"); - generate(buf.as_mut().as_mut_slice()); if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { From 5cfad3f853280069e2927e4838d9b56980e28e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Sun, 13 Nov 2022 02:37:23 +0100 Subject: [PATCH 126/374] Feature gate UARTE disable --- embassy-nrf/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 67b25498..5726f118 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -269,6 +269,7 @@ pub fn init(config: config::Config) -> Peripherals { time_driver::init(config.time_interrupt_priority); // Disable UARTE (enabled by default for some reason) + #[cfg(feature = "_nrf9160")] unsafe { (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); From 4fe834db2f491179edf28105faf79d6fc89785c6 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sun, 13 Nov 2022 02:48:07 +0100 Subject: [PATCH 127/374] Mono channels --- examples/nrf/src/bin/i2s.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 33b5398d..4b6f8ab2 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -8,7 +8,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000}; +use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels}; use embassy_nrf::pac::ficr::info; use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; @@ -37,6 +37,7 @@ async fn main(_spawner: Spawner) { freq: MckFreq::_32MDiv10, ratio: Ratio::_256x, }; // 12500 Hz + config.channels = Channels::Left; let sample_rate = config.mode.sample_rate().expect("I2S Master"); let inv_sample_rate = 1.0 / sample_rate as f32; @@ -46,25 +47,24 @@ async fn main(_spawner: Spawner) { let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); const BUF_SAMPLES: usize = 500; - const BUF_SIZE: usize = BUF_SAMPLES * 2; + const BUF_SIZE: usize = BUF_SAMPLES; let mut buf = AlignedBuffer([0i16; BUF_SIZE]); let mut carrier = SineOsc::new(); - carrier.set_frequency(240.0, inv_sample_rate); + carrier.set_frequency(16.0, inv_sample_rate); // let mut modulator = SineOsc::new(); // modulator.set_frequency(0.01, inv_sample_rate); // modulator.set_amplitude(0.2); let mut generate = |buf: &mut [i16]| { - for sample in buf.as_mut().chunks_mut(2) { + for sample in buf.as_mut() { let signal = carrier.generate(); // let modulation = bipolar_to_unipolar(modulator.generate()); // carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); // carrier.set_amplitude((modulation); let value = (i16::MAX as f32 * signal) as i16; - sample[0] = value; - sample[1] = value; + *sample = value; } }; From eba42cb5f4c4dc1be54c27729325e982d85fc8b0 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sun, 13 Nov 2022 22:15:19 +0100 Subject: [PATCH 128/374] embassy-nrf: Add TWIS module --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/chips/nrf5340_app.rs | 5 + embassy-nrf/src/chips/nrf5340_net.rs | 1 + embassy-nrf/src/chips/nrf9160.rs | 5 + embassy-nrf/src/lib.rs | 7 + embassy-nrf/src/twis.rs | 734 +++++++++++++++++++++++++++ examples/nrf/src/bin/twis.rs | 45 ++ 13 files changed, 815 insertions(+) create mode 100644 embassy-nrf/src/twis.rs create mode 100644 examples/nrf/src/bin/twis.rs diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index dec31a84..0630c0fb 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -133,6 +133,8 @@ impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); +impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index e57a4a38..3867fbd9 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -139,6 +139,8 @@ impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); +impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); + impl_pwm!(PWM0, PWM0, PWM0); impl_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 918404cf..36efd1db 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -140,6 +140,8 @@ impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1); impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); +impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); + impl_pwm!(PWM0, PWM0, PWM0); impl_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index dba033b0..33a07bbc 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -139,6 +139,9 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 81e66c19..b1c33c39 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -149,6 +149,9 @@ impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 92499e3c..db0a87bd 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -177,6 +177,9 @@ impl_spim!(SPI3, SPIM3, SPIM3); impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba..3f4c8b8f 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -180,6 +180,9 @@ impl_spim!(SPI3, SPIM3, SPIM3); impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 7845d4a8..632c02cc 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -366,6 +366,11 @@ impl_twim!(UARTETWISPI1, TWIM1, SERIAL1); impl_twim!(UARTETWISPI2, TWIM2, SERIAL2); impl_twim!(UARTETWISPI3, TWIM3, SERIAL3); +impl_twis!(UARTETWISPI0, TWIS0, SERIAL0); +impl_twis!(UARTETWISPI1, TWIS1, SERIAL1); +impl_twis!(UARTETWISPI2, TWIS2, SERIAL2); +impl_twis!(UARTETWISPI3, TWIS3, SERIAL3); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index ae136e09..917d1a86 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -239,6 +239,7 @@ embassy_hal_common::peripherals! { impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); impl_spim!(UARTETWISPI0, SPIM0, SERIAL0); impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); +impl_twis!(UARTETWISPI0, TWIS0, SERIAL0); impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index b5a53ed8..70285531 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -280,6 +280,11 @@ impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); impl_twim!(UARTETWISPI3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_twis!(UARTETWISPI0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_twis!(UARTETWISPI1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_twis!(UARTETWISPI2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_twis!(UARTETWISPI3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f..6c5a3202 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -100,6 +100,7 @@ pub mod spim; pub mod temp; pub mod timer; pub mod twim; +pub mod twis; pub mod uarte; #[cfg(any( feature = "_nrf5340-app", @@ -267,5 +268,11 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); + // Disable UARTE (enabled by default for some reason) + #[cfg(feature = "_nrf9160")] + unsafe { + (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); + (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); + } peripherals } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs new file mode 100644 index 00000000..8c9cb54e --- /dev/null +++ b/embassy-nrf/src/twis.rs @@ -0,0 +1,734 @@ +#![macro_use] + +//! HAL interface to the TWIS peripheral. +//! +//! See product specification: +//! +//! - nRF52832: Section 33 +//! - nRF52840: Section 6.31 +use core::future::{poll_fn, Future}; +use core::sync::atomic::compiler_fence; +use core::sync::atomic::Ordering::SeqCst; +use core::task::Poll; + +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +#[cfg(feature = "time")] +use embassy_time::{Duration, Instant}; + +use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; +use crate::gpio::Pin as GpioPin; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::util::slice_in_ram_or; +use crate::{gpio, pac, Peripheral}; + +#[non_exhaustive] +pub struct Config { + pub addr0: u8, + pub addr1: Option, + pub orc: u8, + pub sda_high_drive: bool, + pub sda_pullup: bool, + pub scl_high_drive: bool, + pub scl_pullup: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + addr0: 0x55, + addr1: None, + orc: 0x00, + scl_high_drive: false, + sda_pullup: false, + sda_high_drive: false, + scl_pullup: false, + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum Status { + Read, + Write, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + TxBufferTooLong, + RxBufferTooLong, + DataNack, + Bus, + DMABufferNotInDataMemory, + Overflow, + OverRead, + Timeout, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Command { + Read, + WriteRead(usize), + Write(usize), +} + +/// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. +pub struct Twis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Twis<'d, T> { + pub fn new( + twis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + sda: impl Peripheral

+ 'd, + scl: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(twis, irq, sda, scl); + + let r = T::regs(); + + // Configure pins + sda.conf().write(|w| { + w.dir().input(); + w.input().connect(); + if config.sda_high_drive { + w.drive().h0d1(); + } else { + w.drive().s0d1(); + } + if config.sda_pullup { + w.pull().pullup(); + } + w + }); + scl.conf().write(|w| { + w.dir().input(); + w.input().connect(); + if config.scl_high_drive { + w.drive().h0d1(); + } else { + w.drive().s0d1(); + } + if config.scl_pullup { + w.pull().pullup(); + } + w + }); + + // Select pins. + r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) }); + r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) }); + + // Enable TWIS instance. + r.enable.write(|w| w.enable().enabled()); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + + // Set address + r.address[0].write(|w| unsafe { w.address().bits(config.addr0) }); + r.config.modify(|_r, w| w.address0().enabled()); + if let Some(addr1) = config.addr1 { + r.address[1].write(|w| unsafe { w.address().bits(addr1) }); + r.config.modify(|_r, w| w.address1().enabled()); + } + + // Set over-read character + r.orc.write(|w| unsafe { w.orc().bits(config.orc) }); + + // Generate suspend on read event + r.shorts.write(|w| w.read_suspend().enabled()); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self { _p: twis } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.read().clear().write().clear()); + } + if r.events_stopped.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.error().clear()); + } + } + + /// Set TX buffer, checking that it is in RAM and has suitable length. + unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::TxBufferTooLong); + } + + let r = T::regs(); + + r.txd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + w.ptr().bits(buffer.as_ptr() as u32)); + r.txd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + w.maxcnt().bits(buffer.len() as _)); + + Ok(()) + } + + /// Set RX buffer, checking that it has suitable length. + unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // NOTE: RAM slice check is not necessary, as a mutable + // slice can only be built from data located in RAM. + + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } + + let r = T::regs(); + + r.rxd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + w.ptr().bits(buffer.as_mut_ptr() as u32)); + r.rxd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to the type of maxcnt + // is also fine. + // + // Note that that nrf52840 maxcnt is a wider + // type than a u8, so we use a `_` cast rather than a `u8` cast. + // The MAXCNT field is thus at least 8 bits wide and accepts the + // full range of values that fit in a `u8`. + w.maxcnt().bits(buffer.len() as _)); + + Ok(()) + } + + fn clear_errorsrc(&mut self) { + let r = T::regs(); + r.errorsrc + .write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true)); + } + + /// Wait for stop or error + fn blocking_listen_wait(&mut self) -> Result { + let r = T::regs(); + loop { + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + while r.events_stopped.read().bits() == 0 {} + return Err(Error::Overflow); + } + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Err(Error::Bus); + } + if r.events_read.read().bits() != 0 { + r.events_read.reset(); + return Ok(Status::Read); + } + if r.events_write.read().bits() != 0 { + r.events_write.reset(); + return Ok(Status::Write); + } + } + } + + /// Wait for stop or error + fn blocking_listen_wait_end(&mut self, status: Status) -> Result { + let r = T::regs(); + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Overflow); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return match status { + Status::Read => Ok(Command::Read), + Status::Write => { + let n = r.rxd.amount.read().bits() as usize; + Ok(Command::Write(n)) + } + }; + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + let n = r.rxd.amount.read().bits() as usize; + return Ok(Command::WriteRead(n)); + } + } + } + + /// Wait for stop or error + fn blocking_wait(&mut self) -> Result<(), Error> { + let r = T::regs(); + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Err(Error::OverRead); + } else if errorsrc.dnack().is_received() { + return Err(Error::DataNack); + } else { + return Err(Error::Bus); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Ok(()); + } + } + } + + /// Wait for stop or error + #[cfg(feature = "time")] + fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + while r.events_stopped.read().bits() == 0 {} + return Err(Error::Overflow); + } + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Err(Error::Bus); + } + if r.events_read.read().bits() != 0 { + r.events_read.reset(); + return Ok(Status::Read); + } + if r.events_write.read().bits() != 0 { + r.events_write.reset(); + return Ok(Status::Write); + } + if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for stop or error + #[cfg(feature = "time")] + fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Overflow); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return match status { + Status::Read => Ok(Command::Read), + Status::Write => { + let n = r.rxd.amount.read().bits() as usize; + Ok(Command::Write(n)) + } + }; + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + let n = r.rxd.amount.read().bits() as usize; + return Ok(Command::WriteRead(n)); + } else if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for stop or error + #[cfg(feature = "time")] + fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Err(Error::OverRead); + } else if errorsrc.dnack().is_received() { + return Err(Error::DataNack); + } else { + return Err(Error::Bus); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Ok(()); + } else if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for stop or error + fn async_wait(&mut self) -> impl Future> { + poll_fn(move |cx| { + let r = T::regs(); + let s = T::state(); + + s.waker.register(cx.waker()); + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Poll::Ready(Err(Error::OverRead)); + } else if errorsrc.dnack().is_received() { + return Poll::Ready(Err(Error::DataNack)); + } else { + return Poll::Ready(Err(Error::Bus)); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Poll::Ready(Ok(())); + } + + Poll::Pending + }) + } + + /// Wait for read or write + fn async_listen_wait(&mut self) -> impl Future> { + poll_fn(move |cx| { + let r = T::regs(); + let s = T::state(); + + s.waker.register(cx.waker()); + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Poll::Ready(Err(Error::Overflow)); + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + return Poll::Ready(Ok(Status::Read)); + } else if r.events_write.read().bits() != 0 { + r.events_write.reset(); + return Poll::Ready(Ok(Status::Write)); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Poll::Ready(Err(Error::Bus)); + } + Poll::Pending + }) + } + + /// Wait for stop or error + fn async_listen_wait_end(&mut self, status: Status) -> impl Future> { + poll_fn(move |cx| { + let r = T::regs(); + let s = T::state(); + + s.waker.register(cx.waker()); + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Poll::Ready(Err(Error::Overflow)); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return match status { + Status::Read => Poll::Ready(Ok(Command::Read)), + Status::Write => { + let n = r.rxd.amount.read().bits() as usize; + Poll::Ready(Ok(Command::Write(n))) + } + }; + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + let n = r.rxd.amount.read().bits() as usize; + return Poll::Ready(Ok(Command::WriteRead(n))); + } + Poll::Pending + }) + } + + fn setup_write_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { + let r = T::regs(); + + compiler_fence(SeqCst); + + // Set up the DMA write. + unsafe { self.set_tx_buffer(buffer)? }; + + // Clear events + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + if inten { + r.intenset.write(|w| w.stopped().set().error().set()); + } else { + r.intenclr.write(|w| w.stopped().clear().error().clear()); + } + + // Start write operation. + r.tasks_preparetx.write(|w| unsafe { w.bits(1) }); + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + Ok(()) + } + + fn setup_write(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { + match self.setup_write_from_ram(wr_buffer, inten) { + Ok(_) => Ok(()), + Err(Error::DMABufferNotInDataMemory) => { + trace!("Copying TWIS tx buffer into RAM for DMA"); + let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; + tx_ram_buf.copy_from_slice(wr_buffer); + self.setup_write_from_ram(&tx_ram_buf, inten) + } + Err(error) => Err(error), + } + } + + fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> { + let r = T::regs(); + compiler_fence(SeqCst); + + // Set up the DMA read. + unsafe { self.set_rx_buffer(buffer)? }; + + // Clear events + r.events_read.reset(); + r.events_write.reset(); + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + if inten { + r.intenset + .write(|w| w.stopped().set().error().set().read().set().write().set()); + } else { + r.intenclr + .write(|w| w.stopped().clear().error().clear().read().clear().write().clear()); + } + + // Start read operation. + r.tasks_preparerx.write(|w| unsafe { w.bits(1) }); + + Ok(()) + } + + fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> { + let r = T::regs(); + compiler_fence(SeqCst); + + // Clear events + r.events_read.reset(); + r.events_write.reset(); + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + if inten { + r.intenset.write(|w| w.stopped().set().error().set().read().set()); + } else { + r.intenclr.write(|w| w.stopped().clear().error().clear().read().clear()); + } + + Ok(()) + } + + /// Listen for commands from an I2C master. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result { + self.setup_listen(buffer, false)?; + let status = self.blocking_listen_wait()?; + if status == Status::Write { + self.setup_listen_end(false)?; + let command = self.blocking_listen_wait_end(status)?; + return Ok(command); + } + Ok(Command::Read) + } + + /// Write to an I2C master. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write(buffer, false)?; + self.blocking_wait()?; + Ok(()) + } + + /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write_from_ram(buffer, false)?; + self.blocking_wait()?; + Ok(()) + } + + // =========================================== + + /// Listen for commands from an I2C master. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + #[cfg(feature = "time")] + pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result { + self.setup_listen(buffer, false)?; + let status = self.blocking_listen_wait_timeout(timeout)?; + if status == Status::Write { + self.setup_listen_end(false)?; + let command = self.blocking_listen_wait_end_timeout(status, timeout)?; + return Ok(command); + } + Ok(Command::Read) + } + + /// Write to an I2C master with timeout. + /// + /// See [`blocking_write`]. + #[cfg(feature = "time")] + pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + self.setup_write(buffer, false)?; + self.blocking_wait_timeout(timeout)?; + Ok(()) + } + + /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + #[cfg(feature = "time")] + pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + self.setup_write_from_ram(buffer, false)?; + self.blocking_wait_timeout(timeout)?; + Ok(()) + } + + // =========================================== + + pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { + self.setup_listen(buffer, true)?; + let status = self.async_listen_wait().await?; + if status == Status::Write { + self.setup_listen_end(true)?; + let command = self.async_listen_wait_end(status).await?; + return Ok(command); + } + Ok(Command::Read) + } + + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write(buffer, true)?; + self.async_wait().await?; + Ok(()) + } + + /// Same as [`write`](Twis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write_from_ram(buffer, true)?; + self.async_wait().await?; + Ok(()) + } +} + +impl<'a, T: Instance> Drop for Twis<'a, T> { + fn drop(&mut self) { + trace!("twis drop"); + + // TODO: check for abort + + // disable! + let r = T::regs(); + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.sda.read().bits()); + gpio::deconfigure_pin(r.psel.scl.read().bits()); + + trace!("twis drop: done"); + } +} + +pub(crate) mod sealed { + use super::*; + + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static pac::twis0::RegisterBlock; + fn state() -> &'static State; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + 'static { + type Interrupt: Interrupt; +} + +macro_rules! impl_twis { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::twis::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::twis0::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::twis::sealed::State { + static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new(); + &STATE + } + } + impl crate::twis::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs new file mode 100644 index 00000000..f85173c0 --- /dev/null +++ b/examples/nrf/src/bin/twis.rs @@ -0,0 +1,45 @@ +//! TWIS example + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::twis::{self, Command, Twis}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); + let mut config = twis::Config::default(); + // Set i2c address + config.addr0 = 0x55; + let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + + info!("Listening..."); + loop { + let mut buf = [0u8; 16]; + let tx_buf = [1, 2, 3, 4, 5, 6, 7, 8]; + match i2c.listen(&mut buf).await { + Ok(Command::Read) => { + info!("Got READ command. Writing back data:\n{:?}\n", tx_buf); + if let Err(e) = i2c.write(&tx_buf).await { + error!("{:?}", e); + } + } + Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]), + Ok(Command::WriteRead(n)) => { + info!("Got WRITE/READ command with data:\n{:?}", buf[..n]); + info!("Writing back data:\n{:?}\n", tx_buf); + if let Err(e) = i2c.write(&tx_buf).await { + error!("{:?}", e); + } + } + Err(e) => error!("{:?}", e), + } + } +} From 43c1afb6a6b31a60f43b2faf8ca93bf5129e4d68 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 11:22:14 +0100 Subject: [PATCH 129/374] Return number of bytes written, add address match getter --- embassy-nrf/src/twis.rs | 141 +++++++++++++++++++---------------- examples/nrf/src/bin/twis.rs | 2 +- 2 files changed, 77 insertions(+), 66 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 8c9cb54e..76952287 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -24,8 +24,8 @@ use crate::{gpio, pac, Peripheral}; #[non_exhaustive] pub struct Config { - pub addr0: u8, - pub addr1: Option, + pub address0: u8, + pub address1: Option, pub orc: u8, pub sda_high_drive: bool, pub sda_pullup: bool, @@ -36,8 +36,8 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - addr0: 0x55, - addr1: None, + address0: 0x55, + address1: None, orc: 0x00, scl_high_drive: false, sda_pullup: false, @@ -134,10 +134,10 @@ impl<'d, T: Instance> Twis<'d, T> { r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // Set address - r.address[0].write(|w| unsafe { w.address().bits(config.addr0) }); + r.address[0].write(|w| unsafe { w.address().bits(config.address0) }); r.config.modify(|_r, w| w.address0().enabled()); - if let Some(addr1) = config.addr1 { - r.address[1].write(|w| unsafe { w.address().bits(addr1) }); + if let Some(address1) = config.address1 { + r.address[1].write(|w| unsafe { w.address().bits(address1) }); r.config.modify(|_r, w| w.address1().enabled()); } @@ -242,7 +242,13 @@ impl<'d, T: Instance> Twis<'d, T> { .write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true)); } - /// Wait for stop or error + /// Returns matched address for latest command. + pub fn address_match(&self) -> u8 { + let r = T::regs(); + r.address[r.match_.read().bits() as usize].read().address().bits() + } + + /// Wait for read, write, stop or error fn blocking_listen_wait(&mut self) -> Result { let r = T::regs(); loop { @@ -267,7 +273,7 @@ impl<'d, T: Instance> Twis<'d, T> { } } - /// Wait for stop or error + /// Wait for stop, repeated start or error fn blocking_listen_wait_end(&mut self, status: Status) -> Result { let r = T::regs(); loop { @@ -294,7 +300,7 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Wait for stop or error - fn blocking_wait(&mut self) -> Result<(), Error> { + fn blocking_wait(&mut self) -> Result { let r = T::regs(); loop { // stop if an error occured @@ -311,12 +317,42 @@ impl<'d, T: Instance> Twis<'d, T> { } } else if r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); - return Ok(()); + let n = r.txd.amount.read().bits() as usize; + return Ok(n); } } } - /// Wait for stop or error + /// Wait for stop or error with timeout + #[cfg(feature = "time")] + fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Err(Error::OverRead); + } else if errorsrc.dnack().is_received() { + return Err(Error::DataNack); + } else { + return Err(Error::Bus); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + let n = r.txd.amount.read().bits() as usize; + return Ok(n); + } else if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for read, write, stop or error with timeout #[cfg(feature = "time")] fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result { let r = T::regs(); @@ -347,7 +383,7 @@ impl<'d, T: Instance> Twis<'d, T> { } } - /// Wait for stop or error + /// Wait for stop, repeated start or error with timeout #[cfg(feature = "time")] fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result { let r = T::regs(); @@ -379,35 +415,7 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Wait for stop or error - #[cfg(feature = "time")] - fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> { - let r = T::regs(); - let deadline = Instant::now() + timeout; - loop { - // stop if an error occured - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - let errorsrc = r.errorsrc.read(); - if errorsrc.overread().is_detected() { - return Err(Error::OverRead); - } else if errorsrc.dnack().is_received() { - return Err(Error::DataNack); - } else { - return Err(Error::Bus); - } - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); - return Ok(()); - } else if Instant::now() > deadline { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - return Err(Error::Timeout); - } - } - } - - /// Wait for stop or error - fn async_wait(&mut self) -> impl Future> { + fn async_wait(&mut self) -> impl Future> { poll_fn(move |cx| { let r = T::regs(); let s = T::state(); @@ -428,7 +436,8 @@ impl<'d, T: Instance> Twis<'d, T> { } } else if r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); - return Poll::Ready(Ok(())); + let n = r.txd.amount.read().bits() as usize; + return Poll::Ready(Ok(n)); } Poll::Pending @@ -462,7 +471,7 @@ impl<'d, T: Instance> Twis<'d, T> { }) } - /// Wait for stop or error + /// Wait for stop, repeated start or error fn async_listen_wait_end(&mut self, status: Status) -> impl Future> { poll_fn(move |cx| { let r = T::regs(); @@ -595,25 +604,23 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Write to an I2C master. - /// + /// Returns the number of bytes written. /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { self.setup_write(buffer, false)?; - self.blocking_wait()?; - Ok(()) + self.blocking_wait() } /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result { self.setup_write_from_ram(buffer, false)?; - self.blocking_wait()?; - Ok(()) + self.blocking_wait() } // =========================================== - /// Listen for commands from an I2C master. + /// Listen for commands from an I2C master with timeout. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. @@ -630,25 +637,27 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Write to an I2C master with timeout. - /// + /// Returns the number of bytes written. /// See [`blocking_write`]. #[cfg(feature = "time")] - pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { self.setup_write(buffer, false)?; - self.blocking_wait_timeout(timeout)?; - Ok(()) + self.blocking_wait_timeout(timeout) } /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. #[cfg(feature = "time")] - pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { self.setup_write_from_ram(buffer, false)?; - self.blocking_wait_timeout(timeout)?; - Ok(()) + self.blocking_wait_timeout(timeout) } // =========================================== + /// Listen asynchronously for commands from an I2C master. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { self.setup_listen(buffer, true)?; let status = self.async_listen_wait().await?; @@ -660,17 +669,19 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + /// Async write to an I2C master. + /// Returns the number of bytes written. + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + pub async fn write(&mut self, buffer: &[u8]) -> Result { self.setup_write(buffer, true)?; - self.async_wait().await?; - Ok(()) + self.async_wait().await } /// Same as [`write`](Twis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result { self.setup_write_from_ram(buffer, true)?; - self.async_wait().await?; - Ok(()) + self.async_wait().await } } diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs index f85173c0..a34bb271 100644 --- a/examples/nrf/src/bin/twis.rs +++ b/examples/nrf/src/bin/twis.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); let mut config = twis::Config::default(); // Set i2c address - config.addr0 = 0x55; + config.address0 = 0x55; let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); info!("Listening..."); From 8d2d5a30a519de76c3c74e5e8066ac3bc9aa8f77 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 11:39:55 +0100 Subject: [PATCH 130/374] Single waker --- embassy-nrf/src/spis.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 416db2d3..f3105007 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -201,12 +201,12 @@ impl<'d, T: Instance> Spis<'d, T> { let s = T::state(); if r.events_end.read().bits() != 0 { - s.end_waker.wake(); + s.waker.wake(); r.intenclr.write(|w| w.end().clear()); } if r.events_acquired.read().bits() != 0 { - s.acquire_waker.wake(); + s.waker.wake(); r.intenclr.write(|w| w.acquired().clear()); } } @@ -295,7 +295,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Wait until CPU has acquired the semaphore. poll_fn(|cx| { - s.acquire_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.semstat.read().bits() == 1 { return Poll::Ready(()); } @@ -310,7 +310,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Wait for 'end' event. r.intenset.write(|w| w.end().set()); poll_fn(|cx| { - s.end_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.events_end.read().bits() != 0 { return Poll::Ready(()); } @@ -451,15 +451,13 @@ pub(crate) mod sealed { use super::*; pub struct State { - pub end_waker: AtomicWaker, - pub acquire_waker: AtomicWaker, + pub waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - end_waker: AtomicWaker::new(), - acquire_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), } } } From 3a1ddd66c64db1c5efd79b89efd76ac97d9eccce Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 16:18:11 +0100 Subject: [PATCH 131/374] Cleanup interrupts --- embassy-nrf/src/spis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index f3105007..d382ee0c 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -297,9 +297,9 @@ impl<'d, T: Instance> Spis<'d, T> { poll_fn(|cx| { s.waker.register(cx.waker()); if r.semstat.read().bits() == 1 { + r.events_acquired.reset(); return Poll::Ready(()); } - r.intenset.write(|w| w.acquired().set()); Poll::Pending }) .await; @@ -312,9 +312,9 @@ impl<'d, T: Instance> Spis<'d, T> { poll_fn(|cx| { s.waker.register(cx.waker()); if r.events_end.read().bits() != 0 { + r.events_end.reset(); return Poll::Ready(()); } - r.intenset.write(|w| w.end().set()); Poll::Pending }) .await; From 0b066b22d11ee07ed64906b27e950ba83368aa49 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 16:24:21 +0100 Subject: [PATCH 132/374] Check events_acquired --- embassy-nrf/src/spis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index d382ee0c..f6f7db3a 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -296,7 +296,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Wait until CPU has acquired the semaphore. poll_fn(|cx| { s.waker.register(cx.waker()); - if r.semstat.read().bits() == 1 { + if r.events_acquired.read().bits() == 1 { r.events_acquired.reset(); return Poll::Ready(()); } From bcec55464f22069c45f07745efdd0fa45a4959c4 Mon Sep 17 00:00:00 2001 From: Johannes Neyer Date: Fri, 11 Nov 2022 11:06:38 +0100 Subject: [PATCH 133/374] [doc] Fix line indices of basic example --- docs/modules/ROOT/pages/basic_application.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index 4dc4a635..f20ddf53 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -21,7 +21,7 @@ Then, what follows are some declarations on how to deal with panics and faults. [source,rust] ---- -include::example$basic/src/main.rs[lines="11..12"] +include::example$basic/src/main.rs[lines="10"] ---- === Task declaration @@ -30,7 +30,7 @@ After a bit of import declaration, the tasks run by the application should be de [source,rust] ---- -include::example$basic/src/main.rs[lines="13..22"] +include::example$basic/src/main.rs[lines="12..20"] ---- An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking. @@ -45,7 +45,7 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera [source,rust] ---- -include::example$basic/src/main.rs[lines="23..-1"] +include::example$basic/src/main.rs[lines="22..-1"] ---- `#[embassy_executor::main]` takes an optional `config` parameter specifying a function that returns an instance of HAL's `Config` struct. For example: @@ -76,7 +76,7 @@ The project definition needs to contain the embassy dependencies: [source,toml] ---- -include::example$basic/Cargo.toml[lines="8..9"] +include::example$basic/Cargo.toml[lines="9..11"] ---- Depending on your microcontroller, you may need to replace `embassy-nrf` with something else (`embassy-stm32` for STM32. Remember to update feature flags as well). From ea61c192807df7a1422ea04480cca0562cbc3bb2 Mon Sep 17 00:00:00 2001 From: Johannes Neyer Date: Tue, 15 Nov 2022 10:07:35 +0100 Subject: [PATCH 134/374] [doc] Fix some grammar --- docs/modules/ROOT/pages/basic_application.adoc | 2 +- docs/modules/ROOT/pages/layer_by_layer.adoc | 8 ++++---- docs/modules/ROOT/pages/stm32.adoc | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index f20ddf53..d233d77c 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -61,7 +61,7 @@ async fn main(_spawner: Spawner, p: embassy_nrf::Peripherals) { } ``` -What happens when the `blinker` task have been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following: +What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following: . Creates an Embassy Executor . Initializes the microcontroller HAL to get the `Peripherals` diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/modules/ROOT/pages/layer_by_layer.adoc index a96dd9fe..a78a64a9 100644 --- a/docs/modules/ROOT/pages/layer_by_layer.adoc +++ b/docs/modules/ROOT/pages/layer_by_layer.adoc @@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh == PAC version -The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provide distinct types +The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code. Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use. @@ -20,13 +20,13 @@ The blinky app using PAC is shown below: include::example$layer-by-layer/blinky-pac/src/main.rs[] ---- -As you can see, there are a lot of code needed to enable the peripheral clocks, configuring the input pins and the output pins of the application. +As you can see, a lot of code is needed to enable the peripheral clocks and to configure the input pins and the output pins of the application. Another downside of this application is that it is busy-looping while polling the button state. This prevents the microcontroller from utilizing any sleep mode to save power. == HAL version -To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such +To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such as: * Automatically enabling the peripheral clock when you're using the peripheral * Deriving and applying register configuration from higher level types @@ -39,7 +39,7 @@ The HAL example is shown below: include::example$layer-by-layer/blinky-hal/src/main.rs[] ---- -As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` hides all the details accessing the GPIO registers, and allow you to use a much simpler API to query the state of the button and toggle the LED output accordingly. +As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` types hide all the details of accessing the GPIO registers and allow you to use a much simpler API for querying the state of the button and toggling the LED output. The same downside from the PAC example still applies though: the application is busy looping and consuming more power than necessary. diff --git a/docs/modules/ROOT/pages/stm32.adoc b/docs/modules/ROOT/pages/stm32.adoc index 8ed9ab04..7bfc0592 100644 --- a/docs/modules/ROOT/pages/stm32.adoc +++ b/docs/modules/ROOT/pages/stm32.adoc @@ -4,9 +4,9 @@ The link:https://github.com/embassy-rs/embassy/tree/master/embassy-stm32[Embassy == The infinite variant problem -STM32 microcontrollers comes in many families and flavors, and supporting all of them is a big undertaking. Embassy has taken advantage of the fact +STM32 microcontrollers come in many families, and flavors and supporting all of them is a big undertaking. Embassy has taken advantage of the fact that the STM32 peripheral versions are shared across chip families. Instead of re-implementing the SPI -peripheral for every STM32 chip family, embassy have a single SPI implementation that depends on +peripheral for every STM32 chip family, embassy has a single SPI implementation that depends on code-generated register types that are identical for STM32 families with the same version of a given peripheral. === The metapac From 9505a6f7527cee9af73117e49322169f2d568a39 Mon Sep 17 00:00:00 2001 From: Johannes Neyer Date: Tue, 15 Nov 2022 10:07:45 +0100 Subject: [PATCH 135/374] [doc] Remove obsolete code sample --- docs/modules/ROOT/pages/basic_application.adoc | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index d233d77c..3f4f16e2 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -48,19 +48,6 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera include::example$basic/src/main.rs[lines="22..-1"] ---- -`#[embassy_executor::main]` takes an optional `config` parameter specifying a function that returns an instance of HAL's `Config` struct. For example: - -```rust -fn embassy_config() -> embassy_nrf::config::Config { - embassy_nrf::config::Config::default() -} - -#[embassy_executor::main(config = "embassy_config()")] -async fn main(_spawner: Spawner, p: embassy_nrf::Peripherals) { - // ... -} -``` - What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following: . Creates an Embassy Executor From 551b54ddcbb104f881a3333215f4a828b33d029a Mon Sep 17 00:00:00 2001 From: Jaxter Kim Date: Mon, 14 Nov 2022 14:56:30 +0100 Subject: [PATCH 136/374] stm32g0: Fix ADC for channels above 14 --- embassy-stm32/src/adc/v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8f81cb7a..90aa7d3b 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -71,7 +71,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(adc_g0)] T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(true); + reg.set_chselrmod(false); }); } @@ -200,7 +200,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(not(stm32g0))] T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); #[cfg(stm32g0)] - T::regs().chselr().write(|reg| reg.set_chsel(pin.channel() as u32)); + T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be From eb149a0bd42d7690e78e5f2b37579c1f68be613b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 15 Nov 2022 16:12:07 +0100 Subject: [PATCH 137/374] embassy-rp: Add basic ADC module --- embassy-rp/src/adc.rs | 178 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 3 + examples/rp/src/bin/adc.rs | 33 +++++++ 3 files changed, 214 insertions(+) create mode 100644 embassy-rp/src/adc.rs create mode 100644 examples/rp/src/bin/adc.rs diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs new file mode 100644 index 00000000..acef4dea --- /dev/null +++ b/embassy-rp/src/adc.rs @@ -0,0 +1,178 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::into_ref; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal_02::adc::{Channel, OneShot}; + +use crate::interrupt::{self, InterruptExt}; +use crate::peripherals::ADC; +use crate::{pac, peripherals, Peripheral}; +static WAKER: AtomicWaker = AtomicWaker::new(); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now +} + +#[non_exhaustive] +pub struct Config {} + +impl Default for Config { + fn default() -> Self { + Self {} + } +} +pub struct Adc<'d> { + phantom: PhantomData<&'d ADC>, +} + +impl<'d> Adc<'d> { + #[inline] + fn regs() -> pac::adc::Adc { + pac::ADC + } + + #[inline] + fn reset() -> pac::resets::regs::Peripherals { + let mut ret = pac::resets::regs::Peripherals::default(); + ret.set_adc(true); + ret + } + + pub fn new( + _inner: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + _config: Config, + ) -> Self { + into_ref!(irq); + unsafe { + let reset = Self::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + let r = Self::regs(); + // Enable ADC + r.cs().write(|w| w.set_en(true)); + // Wait for ADC ready + while !r.cs().read().ready() {} + } + + // Setup IRQ + irq.disable(); + irq.set_handler(|_| unsafe { + let r = Self::regs(); + r.inte().modify(|w| w.set_fifo(false)); + WAKER.wake(); + }); + irq.unpend(); + irq.enable(); + + Self { phantom: PhantomData } + } + + async fn wait_for_ready() { + let r = Self::regs(); + unsafe { + r.inte().modify(|w| w.set_fifo(true)); + compiler_fence(Ordering::SeqCst); + poll_fn(|cx| { + WAKER.register(cx.waker()); + if r.cs().read().ready() { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + } + } + + pub async fn read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| { + w.set_ainsel(PIN::channel()); + w.set_start_once(true) + }); + Self::wait_for_ready().await; + r.result().read().result().into() + } + } + + pub async fn read_temperature(&mut self) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| w.set_ts_en(true)); + if !r.cs().read().ready() { + Self::wait_for_ready().await; + } + r.cs().modify(|w| { + w.set_ainsel(4); + w.set_start_once(true) + }); + Self::wait_for_ready().await; + r.result().read().result().into() + } + } + + pub fn blocking_read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + let r = Self::regs(); + let ch = PIN::channel(); + unsafe { + if ch == 4 { + r.cs().modify(|w| w.set_ts_en(true)) + } + while !r.cs().read().ready() {} + r.cs().modify(|w| { + w.set_ainsel(ch); + w.set_start_once(true) + }); + while !r.cs().read().ready() {} + r.result().read().result().into() + } + } + + pub fn blocking_read_temperature(&mut self) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| w.set_ts_en(true)); + while !r.cs().read().ready() {} + r.cs().modify(|w| { + w.set_ainsel(4); + w.set_start_once(true) + }); + while !r.cs().read().ready() {} + r.result().read().result().into() + } + } +} + +macro_rules! impl_pin { + ($pin:ident, $channel:expr) => { + impl Channel> for peripherals::$pin { + type ID = u8; + fn channel() -> u8 { + $channel + } + } + }; +} + +impl_pin!(PIN_26, 0); +impl_pin!(PIN_27, 1); +impl_pin!(PIN_28, 2); +impl_pin!(PIN_29, 3); + +impl OneShot, WORD, PIN> for Adc<'static> +where + WORD: From, + PIN: Channel, ID = u8>, +{ + type Error = (); + fn read(&mut self, pin: &mut PIN) -> nb::Result { + Ok(self.blocking_read(pin).into()) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index f608f176..6c91b1ad 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -6,6 +6,7 @@ pub(crate) mod fmt; mod intrinsics; +pub mod adc; pub mod dma; pub mod gpio; pub mod i2c; @@ -98,6 +99,8 @@ embassy_hal_common::peripherals! { RTC, FLASH, + + ADC, } #[link_section = ".boot2"] diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs new file mode 100644 index 00000000..2a9e9373 --- /dev/null +++ b/examples/rp/src/bin/adc.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::adc::{Adc, Config}; +use embassy_rp::interrupt; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let irq = interrupt::take!(ADC_IRQ_FIFO); + let mut adc = Adc::new(p.ADC, irq, Config::default()); + + let mut p26 = p.PIN_26; + let mut p27 = p.PIN_27; + let mut p28 = p.PIN_28; + + loop { + let level = adc.read(&mut p26).await; + info!("Pin 26 ADC: {}", level); + let level = adc.read(&mut p27).await; + info!("Pin 27 ADC: {}", level); + let level = adc.read(&mut p28).await; + info!("Pin 28 ADC: {}", level); + let temp = adc.read_temperature().await; + info!("Temp: {}", temp); + Timer::after(Duration::from_secs(1)).await; + } +} From 9f870a5edf08c4322a6535d07f6444341fbf4c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 15 Nov 2022 16:31:19 +0100 Subject: [PATCH 138/374] Cleanup --- embassy-rp/src/adc.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index acef4dea..cdb752dc 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -120,14 +120,9 @@ impl<'d> Adc<'d> { pub fn blocking_read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { let r = Self::regs(); - let ch = PIN::channel(); unsafe { - if ch == 4 { - r.cs().modify(|w| w.set_ts_en(true)) - } - while !r.cs().read().ready() {} r.cs().modify(|w| { - w.set_ainsel(ch); + w.set_ainsel(PIN::channel()); w.set_start_once(true) }); while !r.cs().read().ready() {} From 1ed260b1055fad6ddd89053ae3e1997ec34c6332 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 17 Nov 2022 00:19:22 +0100 Subject: [PATCH 139/374] Fix buffer overruns --- embassy-nrf/src/i2s.rs | 427 ++++++++++++++++++++---------------- examples/nrf/src/bin/i2s.rs | 88 +++++--- 2 files changed, 294 insertions(+), 221 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index eed9e195..e6dfb690 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -3,6 +3,7 @@ //! I2S use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -10,7 +11,6 @@ use embassy_cortex_m::interrupt::{InterruptExt, Priority}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -//use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL}; @@ -31,9 +31,9 @@ pub const SRAM_UPPER: usize = 0x3000_0000; pub enum Error { BufferTooLong, BufferZeroLength, - DMABufferNotInDataMemory, + BufferNotInDataMemory, BufferMisaligned, - // TODO: add other error variants. + BufferLengthMisaligned, } pub const MODE_MASTER_8000: Mode = Mode::Master { @@ -122,12 +122,12 @@ pub enum MckFreq { } impl MckFreq { - const REGISTER_VALUES: &[u32] = &[ + const REGISTER_VALUES: &'static [u32] = &[ 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, 0x08000000, 0x06000000, 0x04100000, 0x020C0000, ]; - const FREQUENCIES: &[u32] = &[ + const FREQUENCIES: &'static [u32] = &[ 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, 256000, ]; @@ -162,7 +162,7 @@ pub enum Ratio { } impl Ratio { - const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; + const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; pub fn to_divisor(&self) -> u32 { Self::RATIOS[u8::from(*self) as usize] @@ -234,23 +234,10 @@ impl From for u8 { } } -/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. +/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. pub struct I2S<'d, T: Instance> { - output: I2sOutput<'d, T>, - input: I2sInput<'d, T>, -} - -/// Transmitter interface to the UARTE peripheral obtained -/// via [Uarte]::split. -pub struct I2sOutput<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - -/// Receiver interface to the UARTE peripheral obtained -/// via [Uarte]::split. -pub struct I2sInput<'d, T: Instance> { _p: PeripheralRef<'d, T>, } @@ -298,78 +285,19 @@ impl<'d, T: Instance> I2S<'d, T> { r.enable.write(|w| w.enable().enabled()); - Self { - output: I2sOutput { - _p: unsafe { i2s.clone_unchecked() }, - }, - input: I2sInput { _p: i2s }, - } + Self { _p: i2s } } - /// Enables the I2S module. - #[inline(always)] - pub fn enable(&self) -> &Self { - let r = T::regs(); - r.enable.write(|w| w.enable().enabled()); - self + pub fn output(self) -> Output<'d, T> { + Output { _p: self._p } } - /// Disables the I2S module. - #[inline(always)] - pub fn disable(&self) -> &Self { - let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); - self + pub fn input(self) -> Input<'d, T> { + Input { _p: self._p } } - /// Starts I2S transfer. - #[inline(always)] - pub fn start(&self) -> &Self { - let r = T::regs(); - self.enable(); - trace!("START"); - r.tasks_start.write(|w| unsafe { w.bits(1) }); - self - } - - /// Stops the I2S transfer and waits until it has stopped. - #[inline(always)] - pub async fn stop(&self) { - todo!() - } - - /// Enables/disables I2S transmission (TX). - #[inline(always)] - pub fn set_tx_enabled(&self, enabled: bool) -> &Self { - let r = T::regs(); - r.config.txen.write(|w| w.txen().bit(enabled)); - self - } - - /// Enables/disables I2S reception (RX). - #[inline(always)] - pub fn set_rx_enabled(&self, enabled: bool) -> &Self { - let r = T::regs(); - r.config.rxen.write(|w| w.rxen().bit(enabled)); - self - } - - /// Transmits the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - pub async fn tx(&mut self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - self.output.tx(buffer).await - } - - /// Receives data into the given `buffer` until it's filled. - /// Buffer address must be 4 byte aligned and located in RAM. - pub async fn rx(&mut self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - self.input.rx(buffer).await + pub fn full_duplex(self) -> FullDuplex<'d, T> { + FullDuplex { _p: self._p } } fn apply_config(c: &CONFIG, config: &Config) { @@ -433,103 +361,94 @@ impl<'d, T: Instance> I2S<'d, T> { irq.unpend(); irq.enable(); - r.intenclr.write(|w| w.rxptrupd().clear()); - r.intenclr.write(|w| w.txptrupd().clear()); + let device = Device::::new(); + device.disable_tx_ptr_interrupt(); + device.disable_rx_ptr_interrupt(); - r.events_rxptrupd.reset(); - r.events_txptrupd.reset(); + device.reset_tx_ptr_event(); + device.reset_rx_ptr_event(); - r.intenset.write(|w| w.rxptrupd().set()); - r.intenset.write(|w| w.txptrupd().set()); + device.enable_tx_ptr_interrupt(); + device.enable_rx_ptr_interrupt(); } fn on_interrupt(_: *mut ()) { - let r = T::regs(); + let device = Device::::new(); let s = T::state(); - if r.events_txptrupd.read().bits() != 0 { - trace!("[{}] INT", s.seq.load(Ordering::Relaxed)); + if device.is_tx_ptr_updated() { + trace!("TX INT"); s.tx_waker.wake(); - r.intenclr.write(|w| w.txptrupd().clear()); + device.disable_tx_ptr_interrupt(); } - if r.events_rxptrupd.read().bits() != 0 { + if device.is_rx_ptr_updated() { + trace!("RX INT"); s.rx_waker.wake(); - r.intenclr.write(|w| w.rxptrupd().clear()); + device.disable_rx_ptr_interrupt(); } - - s.overruns.fetch_add(1, Ordering::Relaxed); } } -impl<'d, T: Instance> I2sOutput<'d, T> { - /// Transmits the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - #[allow(unused_mut)] - pub async fn tx(&mut self, buffer: B) -> Result<(), Error> +pub struct Output<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Output<'d, T> { + /// Starts I2S transfer. + #[inline(always)] + pub fn start(&self, buffer: B) -> Result<(), Error> where B: Buffer, { - let ptr = buffer.bytes_ptr(); - let len = buffer.bytes_len(); + // TODO what to do if it is started already? - if ptr as u32 % 4 != 0 { - return Err(Error::BufferMisaligned); - } - if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - return Err(Error::DMABufferNotInDataMemory); - } - let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; - if maxcnt > MAX_DMA_MAXCNT { - return Err(Error::BufferTooLong); - } + let device = Device::::new(); + device.enable(); + device.set_tx_buffer(buffer)?; + device.enable_tx(); + device.start(); - let r = T::regs(); - let s = T::state(); + Ok(()) + } - let seq = s.seq.fetch_add(1, Ordering::Relaxed); - if r.events_txptrupd.read().bits() != 0 && seq > 1 { - warn!("XRUN!"); - loop {} - } + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + todo!() + } - let drop = OnDrop::new(move || { - trace!("write drop: stopping"); + /// Transmits the given `buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + #[allow(unused_mut)] + pub async fn send(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + trace!("SEND: {}", buffer.bytes_ptr() as u32); - r.intenclr.write(|w| w.txptrupd().clear()); - r.events_txptrupd.reset(); - r.config.txen.write(|w| w.txen().disabled()); - - // TX is stopped almost instantly, spinning is fine. - while r.events_txptrupd.read().bits() == 0 {} - trace!("write drop: stopped"); - }); - - trace!("[{}] PTR", s.seq.load(Ordering::Relaxed)); - r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + let device = Device::::new(); + let drop = device.on_tx_drop(); compiler_fence(Ordering::SeqCst); poll_fn(|cx| { - s.tx_waker.register(cx.waker()); - if r.events_txptrupd.read().bits() != 0 || seq == 0 { - trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed)); - r.events_txptrupd.reset(); - r.intenset.write(|w| w.txptrupd().set()); - let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); - if overruns != 0 { - warn!("XRUN: {}", overruns); - s.overruns.store(0, Ordering::Relaxed) - } + T::state().tx_waker.register(cx.waker()); + + if device.is_tx_ptr_updated() { + trace!("TX POLL: Ready"); + device.reset_tx_ptr_event(); + device.enable_tx_ptr_interrupt(); Poll::Ready(()) } else { - trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed)); + trace!("TX POLL: Pending"); Poll::Pending } }) .await; + device.set_tx_buffer(buffer)?; + compiler_fence(Ordering::SeqCst); drop.defuse(); @@ -537,40 +456,180 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } } -impl<'d, T: Instance> I2sInput<'d, T> { - /// Receives into the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - #[allow(unused_mut)] - pub async fn rx(&mut self, buffer: B) -> Result<(), Error> +pub struct Input<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Input<'d, T> { + // TODO +} + +pub struct FullDuplex<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> FullDuplex<'d, T> { + // TODO +} + +struct Device(&'static RegisterBlock, PhantomData); + +impl Device { + fn new() -> Self { + Self(T::regs(), PhantomData) + } + + #[inline(always)] + pub fn enable(&self) { + trace!("ENABLED"); + self.0.enable.write(|w| w.enable().enabled()); + } + + #[inline(always)] + pub fn disable(&self) { + trace!("DISABLED"); + self.0.enable.write(|w| w.enable().disabled()); + } + + #[inline(always)] + fn enable_tx(&self) { + trace!("TX ENABLED"); + self.0.config.txen.write(|w| w.txen().enabled()); + } + + #[inline(always)] + fn disable_tx(&self) { + trace!("TX DISABLED"); + self.0.config.txen.write(|w| w.txen().disabled()); + } + + #[inline(always)] + fn enable_rx(&self) { + trace!("RX ENABLED"); + self.0.config.rxen.write(|w| w.rxen().enabled()); + } + + #[inline(always)] + fn disable_rx(&self) { + trace!("RX DISABLED"); + self.0.config.rxen.write(|w| w.rxen().disabled()); + } + + #[inline(always)] + fn start(&self) { + trace!("START"); + self.0.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + #[inline] + fn set_tx_buffer(&self, buffer: B) -> Result<(), Error> where B: Buffer, { - let ptr = buffer.bytes_ptr(); - let len = buffer.bytes_len(); - - if ptr as u32 % 4 != 0 { - return Err(Error::BufferMisaligned); - } - if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - return Err(Error::DMABufferNotInDataMemory); - } - let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; - if maxcnt > MAX_DMA_MAXCNT { - return Err(Error::BufferTooLong); - } - - let r = T::regs(); - let _s = T::state(); - - // TODO we can not progress until the last buffer written in RXD.PTR - // has started the transmission. - // We can use some sync primitive from `embassy-sync`. - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - + let (ptr, maxcnt) = Self::validate_buffer(buffer)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); Ok(()) } + + #[inline] + fn set_rx_buffer(&self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let (ptr, maxcnt) = Self::validate_buffer(buffer)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + Ok(()) + } + + #[inline(always)] + fn is_tx_ptr_updated(&self) -> bool { + self.0.events_txptrupd.read().bits() != 0 + } + + #[inline(always)] + fn is_rx_ptr_updated(&self) -> bool { + self.0.events_rxptrupd.read().bits() != 0 + } + + #[inline(always)] + fn reset_tx_ptr_event(&self) { + trace!("TX PTR EVENT: Reset"); + self.0.events_txptrupd.reset(); + } + + #[inline(always)] + fn reset_rx_ptr_event(&self) { + trace!("RX PTR EVENT: Reset"); + self.0.events_rxptrupd.reset(); + } + + #[inline(always)] + fn disable_tx_ptr_interrupt(&self) { + trace!("TX PTR INTERRUPT: Disabled"); + self.0.intenclr.write(|w| w.txptrupd().clear()); + } + + #[inline(always)] + fn disable_rx_ptr_interrupt(&self) { + trace!("RX PTR INTERRUPT: Disabled"); + self.0.intenclr.write(|w| w.rxptrupd().clear()); + } + + #[inline(always)] + fn enable_tx_ptr_interrupt(&self) { + trace!("TX PTR INTERRUPT: Enabled"); + self.0.intenset.write(|w| w.txptrupd().set()); + } + + #[inline(always)] + fn enable_rx_ptr_interrupt(&self) { + trace!("RX PTR INTERRUPT: Enabled"); + self.0.intenclr.write(|w| w.rxptrupd().clear()); + } + + #[inline] + fn on_tx_drop(&self) -> OnDrop { + OnDrop::new(move || { + trace!("TX DROP: Stopping"); + + let device = Device::::new(); + device.disable_tx_ptr_interrupt(); + device.reset_tx_ptr_event(); + device.disable_tx(); + + // TX is stopped almost instantly, spinning is fine. + while !device.is_tx_ptr_updated() {} + + trace!("TX DROP: Stopped"); + }) + } + + fn validate_buffer(buffer: B) -> Result<(u32, u32), Error> + where + B: Buffer, + { + let ptr = buffer.bytes_ptr() as u32; + let len = buffer.bytes_len(); + let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + + trace!("PTR={}, MAXCNT={}", ptr, maxcnt); + + // TODO can we avoid repeating all those runtime checks for the same buffer again and again? + + if ptr % 4 != 0 { + Err(Error::BufferMisaligned) + } else if len % 4 != 0 { + Err(Error::BufferLengthMisaligned) + } else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + Err(Error::BufferNotInDataMemory) + } else if maxcnt > MAX_DMA_MAXCNT { + Err(Error::BufferTooLong) + } else { + Ok((ptr, maxcnt)) + } + } } pub trait Buffer: Sized { @@ -578,10 +637,10 @@ pub trait Buffer: Sized { fn bytes_len(&self) -> usize; } -impl Buffer for &[u8] { +impl Buffer for &[i8] { #[inline] fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() + self.as_ptr() as *const u8 } #[inline] @@ -610,7 +669,7 @@ impl Buffer for &[i32] { #[inline] fn bytes_len(&self) -> usize { - self.len() * core::mem::size_of::() + self.len() * core::mem::size_of::() } } @@ -624,8 +683,6 @@ pub(crate) mod sealed { pub struct State { pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, - pub overruns: AtomicI32, - pub seq: AtomicI32, } impl State { @@ -633,8 +690,6 @@ pub(crate) mod sealed { Self { rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), - overruns: AtomicI32::new(0), - seq: AtomicI32::new(0), } } } @@ -654,7 +709,7 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::sealed::Instance for peripherals::$type { - fn regs() -> &'static pac::i2s::RegisterBlock { + fn regs() -> &'static crate::pac::i2s::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } fn state() -> &'static crate::i2s::sealed::State { diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 4b6f8ab2..9b3144f2 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -1,14 +1,13 @@ -// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs - #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use core::f32::consts::PI; -use defmt::{error, info}; +use defmt::{error, info, trace}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels}; +use embassy_nrf::gpio::{Input, Pin, Pull}; +use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000}; use embassy_nrf::pac::ficr::info; use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; @@ -32,60 +31,79 @@ impl AsMut for AlignedBuffer { async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut config = i2s::Config::default(); - // config.mode = MODE_MASTER_16000; - config.mode = Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_256x, - }; // 12500 Hz + config.mode = MODE_MASTER_32000; + // config.mode = Mode::Master { + // freq: MckFreq::_32MDiv10, + // ratio: Ratio::_256x, + // }; // 12500 Hz config.channels = Channels::Left; + config.swidth = SampleWidth::_16bit; let sample_rate = config.mode.sample_rate().expect("I2S Master"); let inv_sample_rate = 1.0 / sample_rate as f32; info!("Sample rate: {}", sample_rate); - let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + // Wait for a button press + // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up); + // btn1.wait_for_low().await; - const BUF_SAMPLES: usize = 500; - const BUF_SIZE: usize = BUF_SAMPLES; - let mut buf = AlignedBuffer([0i16; BUF_SIZE]); + let irq = interrupt::take!(I2S); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output(); + + type Sample = i16; + const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample; + const NUM_SAMPLES: usize = 2000; + let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [ + AlignedBuffer([0; NUM_SAMPLES]), + AlignedBuffer([0; NUM_SAMPLES]), + AlignedBuffer([0; NUM_SAMPLES]), + ]; let mut carrier = SineOsc::new(); - carrier.set_frequency(16.0, inv_sample_rate); - // let mut modulator = SineOsc::new(); - // modulator.set_frequency(0.01, inv_sample_rate); - // modulator.set_amplitude(0.2); + let mut freq_mod = SineOsc::new(); + freq_mod.set_frequency(8.0, inv_sample_rate); + freq_mod.set_amplitude(1.0); - let mut generate = |buf: &mut [i16]| { - for sample in buf.as_mut() { + let mut amp_mod = SineOsc::new(); + amp_mod.set_frequency(4.0, inv_sample_rate); + amp_mod.set_amplitude(0.5); + + let mut generate = |buf: &mut [Sample]| { + let ptr = buf as *const [Sample] as *const Sample as u32; + trace!("GEN: {}", ptr); + + for sample in &mut buf.as_mut().chunks_mut(1) { let signal = carrier.generate(); - // let modulation = bipolar_to_unipolar(modulator.generate()); - // carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); - // carrier.set_amplitude((modulation); - let value = (i16::MAX as f32 * signal) as i16; - *sample = value; + let freq_modulation = bipolar_to_unipolar(freq_mod.generate()); + carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate); + let amp_modulation = bipolar_to_unipolar(amp_mod.generate()); + carrier.set_amplitude(amp_modulation); + let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample; + sample[0] = value; } }; - generate(buf.as_mut().as_mut_slice()); + generate(buffers[0].as_mut().as_mut_slice()); + generate(buffers[1].as_mut().as_mut_slice()); - if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { - error!("{}", err); - } - - i2s.set_tx_enabled(true); - i2s.start(); + i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start"); + let mut index = 1; loop { - generate(buf.as_mut().as_mut_slice()); - - if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { + if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await { error!("{}", err); } + + index += 1; + if index >= 3 { + index = 0; + } + generate(buffers[index].as_mut().as_mut_slice()); } } +#[derive(Clone)] struct SineOsc { amplitude: f32, modulo: f32, From a444a65ebfcea674e74dcedc7f26a7aa37f59c69 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 17 Nov 2022 09:26:15 +0100 Subject: [PATCH 140/374] feat: embassy-usb-logger and example for rpi pico * Add embassy-usb-logger which allows logging over USB for any device implementing embassy-usb * Add example using logger for rpi pico. --- embassy-usb-logger/Cargo.toml | 13 +++ embassy-usb-logger/src/lib.rs | 146 ++++++++++++++++++++++++++++++ examples/rp/Cargo.toml | 2 + examples/rp/src/bin/usb_logger.rs | 30 ++++++ 4 files changed, 191 insertions(+) create mode 100644 embassy-usb-logger/Cargo.toml create mode 100644 embassy-usb-logger/src/lib.rs create mode 100644 examples/rp/src/bin/usb_logger.rs diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml new file mode 100644 index 00000000..2f466592 --- /dev/null +++ b/embassy-usb-logger/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "embassy-usb-logger" +version = "0.1.0" +edition = "2021" + +[dependencies] +embassy-usb = { version = "0.1.0", path = "../embassy-usb" } +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +futures = { version = "0.3", default-features = false } +static_cell = "1" +usbd-hid = "0.6.0" +log = "0.4" diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs new file mode 100644 index 00000000..6386e209 --- /dev/null +++ b/embassy-usb-logger/src/lib.rs @@ -0,0 +1,146 @@ +#![no_std] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] + +use core::fmt::Write as _; + +use embassy_futures::join::join; +use embassy_sync::pipe::Pipe; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::Driver; +use embassy_usb::{Builder, Config}; +use log::{Metadata, Record}; + +type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; + +/// The logger state containing buffers that must live as long as the USB peripheral. +pub struct LoggerState<'d> { + state: State<'d>, + device_descriptor: [u8; 32], + config_descriptor: [u8; 128], + bos_descriptor: [u8; 16], + control_buf: [u8; 64], +} + +impl<'d> LoggerState<'d> { + /// Create a new instance of the logger state. + pub fn new() -> Self { + Self { + state: State::new(), + device_descriptor: [0; 32], + config_descriptor: [0; 128], + bos_descriptor: [0; 16], + control_buf: [0; 64], + } + } +} + +/// The logger handle, which contains a pipe with configurable size for buffering log messages. +pub struct UsbLogger { + buffer: Pipe, +} + +impl UsbLogger { + /// Create a new logger instance. + pub const fn new() -> Self { + Self { buffer: Pipe::new() } + } + + /// Run the USB logger using the state and USB driver. Never returns. + pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> ! + where + D: Driver<'d>, + Self: 'd, + { + const MAX_PACKET_SIZE: u8 = 64; + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial logger"); + config.serial_number = None; + config.max_power = 100; + config.max_packet_size_0 = MAX_PACKET_SIZE; + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + let mut builder = Builder::new( + driver, + config, + &mut state.device_descriptor, + &mut state.config_descriptor, + &mut state.bos_descriptor, + &mut state.control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16); + + // Build the builder. + let mut device = builder.build(); + + loop { + let run_fut = device.run(); + let log_fut = async { + let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + class.wait_connection().await; + loop { + let len = self.buffer.read(&mut rx[..]).await; + let _ = class.write_packet(&rx[..len]).await; + } + }; + join(run_fut, log_fut).await; + } + } +} + +impl log::Log for UsbLogger { + fn enabled(&self, _metadata: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let _ = write!(Writer(&self.buffer), "{}\r\n", record.args()); + } + } + + fn flush(&self) {} +} + +struct Writer<'d, const N: usize>(&'d Pipe); + +impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + let _ = self.0.try_write(s.as_bytes()); + Ok(()) + } +} + +/// Initialize and run the USB serial logger, never returns. +/// +/// Arguments specify the buffer size, log level and the USB driver, respectively. +/// +/// # Usage +/// +/// ``` +/// embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); +/// ``` +/// +/// # Safety +/// +/// This macro should only be invoked only once since it is setting the global logging state of the application. +#[macro_export] +macro_rules! run { + ( $x:expr, $l:expr, $p:ident ) => { + static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); + unsafe { + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level($l)); + } + let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; + }; +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 31f68830..25022886 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,6 +13,7 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt" embassy-usb = { version = "0.1.0", path = "../../embassy-usb", 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" } +embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } defmt = "0.3" defmt-rtt = "0.3" @@ -32,6 +33,7 @@ embedded-hal-async = { version = "0.1.0-alpha.3" } embedded-io = { version = "0.3.1", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" +log = "0.4" [profile.release] debug = true diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs new file mode 100644 index 00000000..52417a02 --- /dev/null +++ b/examples/rp/src/bin/usb_logger.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::Driver; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn logger_task(driver: Driver<'static, USB>) { + embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let irq = interrupt::take!(USBCTRL_IRQ); + let driver = Driver::new(p.USB, irq); + spawner.spawn(logger_task(driver)).unwrap(); + + let mut counter = 0; + loop { + counter += 1; + log::info!("Tick {}", counter); + Timer::after(Duration::from_secs(1)).await; + } +} From 6b88057aef79e32a66c9c99cf048f905d10c2d3a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 00:29:05 +0100 Subject: [PATCH 141/374] Add missing parts and Cleanup --- embassy-nrf/src/i2s.rs | 551 +++++++++++++++--- .../nrf/src/bin/{i2s.rs => i2s-generate.rs} | 73 +-- 2 files changed, 493 insertions(+), 131 deletions(-) rename examples/nrf/src/bin/{i2s.rs => i2s-generate.rs} (62%) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index e6dfb690..53d9f9a1 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -1,27 +1,27 @@ #![macro_use] -//! I2S +//! Support for I2S audio use core::future::poll_fn; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::{InterruptExt, Priority}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; -use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL}; +use crate::pac::i2s::RegisterBlock; use crate::Peripheral; // TODO: Define those in lib.rs somewhere else -// -// I2S EasyDMA MAXCNT bit length = 14 + +/// I2S EasyDMA MAXCNT bit length = 14 const MAX_DMA_MAXCNT: u32 = 1 << 14; -// Limits for Easy DMA - it can only read from data ram +/// Limits for Easy DMA - it can only read from data ram pub const SRAM_LOWER: usize = 0x2000_0000; pub const SRAM_UPPER: usize = 0x3000_0000; @@ -36,35 +36,144 @@ pub enum Error { BufferLengthMisaligned, } -pub const MODE_MASTER_8000: Mode = Mode::Master { - freq: MckFreq::_32MDiv125, - ratio: Ratio::_32x, -}; // error = 0 -pub const MODE_MASTER_11025: Mode = Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_192x, -}; // error = 86 -pub const MODE_MASTER_16000: Mode = Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_96x, -}; // error = 127 -pub const MODE_MASTER_22050: Mode = Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_96x, -}; // error = 172 -pub const MODE_MASTER_32000: Mode = Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_48x, -}; // error = 254 -pub const MODE_MASTER_44100: Mode = Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_48x, -}; // error = 344 -pub const MODE_MASTER_48000: Mode = Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_32x, -}; // error = 381 +/// Approximate sample rates. +/// +/// Those are common sample rates that can not be configured without an small error. +/// +/// For custom master clock configuration, please refer to [Mode]. +#[derive(Clone, Copy)] +pub enum ApproxSampleRate { + _11025, + _16000, + _22050, + _32000, + _44100, + _48000, +} +impl From for Mode { + fn from(value: ApproxSampleRate) -> Self { + match value { + // error = 86 + ApproxSampleRate::_11025 => Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_192x, + }, + // error = 127 + ApproxSampleRate::_16000 => Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_96x, + }, + // error = 172 + ApproxSampleRate::_22050 => Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_96x, + }, + // error = 254 + ApproxSampleRate::_32000 => Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_48x, + }, + // error = 344 + ApproxSampleRate::_44100 => Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_48x, + }, + // error = 381 + ApproxSampleRate::_48000 => Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_32x, + }, + } + } +} + +impl ApproxSampleRate { + pub fn sample_rate(&self) -> u32 { + // This will always provide a Master mode, so it is safe to unwrap. + Mode::from(*self).sample_rate().unwrap() + } +} + +/// Exact sample rates. +/// +/// Those are non standard sample rates that can be configured without error. +/// +/// For custom master clock configuration, please refer to [Mode]. +#[derive(Clone, Copy)] +pub enum ExactSampleRate { + _8000, + _10582, + _12500, + _15625, + _15873, + _25000, + _31250, + _50000, + _62500, + _100000, + _125000, +} + +impl ExactSampleRate { + pub fn sample_rate(&self) -> u32 { + // This will always provide a Master mode, so it is safe to unwrap. + Mode::from(*self).sample_rate().unwrap() + } +} + +impl From for Mode { + fn from(value: ExactSampleRate) -> Self { + match value { + ExactSampleRate::_8000 => Mode::Master { + freq: MckFreq::_32MDiv125, + ratio: Ratio::_32x, + }, + ExactSampleRate::_10582 => Mode::Master { + freq: MckFreq::_32MDiv63, + ratio: Ratio::_48x, + }, + ExactSampleRate::_12500 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_256x, + }, + ExactSampleRate::_15625 => Mode::Master { + freq: MckFreq::_32MDiv32, + ratio: Ratio::_64x, + }, + ExactSampleRate::_15873 => Mode::Master { + freq: MckFreq::_32MDiv63, + ratio: Ratio::_32x, + }, + ExactSampleRate::_25000 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_128x, + }, + ExactSampleRate::_31250 => Mode::Master { + freq: MckFreq::_32MDiv32, + ratio: Ratio::_32x, + }, + ExactSampleRate::_50000 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_64x, + }, + ExactSampleRate::_62500 => Mode::Master { + freq: MckFreq::_32MDiv16, + ratio: Ratio::_32x, + }, + ExactSampleRate::_100000 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_32x, + }, + ExactSampleRate::_125000 => Mode::Master { + freq: MckFreq::_32MDiv8, + ratio: Ratio::_32x, + }, + } + } +} + +/// I2S configuration. #[derive(Clone)] #[non_exhaustive] pub struct Config { @@ -78,7 +187,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - mode: MODE_MASTER_32000, + mode: ExactSampleRate::_31250.into(), swidth: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, @@ -132,10 +241,12 @@ impl MckFreq { 256000, ]; + /// Return the value that needs to be written to the register. pub fn to_register_value(&self) -> u32 { Self::REGISTER_VALUES[usize::from(*self)] } + /// Return the master clock frequency. pub fn to_frequency(&self) -> u32 { Self::FREQUENCIES[usize::from(*self)] } @@ -147,7 +258,10 @@ impl From for usize { } } -/// MCK / LRCK ratio. +/// Master clock frequency ratio +/// +/// Sample Rate = LRCK = MCK / Ratio +/// #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Ratio { _32x, @@ -175,6 +289,7 @@ impl From for u8 { } } +/// Sample width. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SampleWidth { _8bit, @@ -188,7 +303,7 @@ impl From for u8 { } } -/// Alignment of sample within a frame. +/// Channel used for the most significant sample value in a frame. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Align { Left, @@ -220,11 +335,13 @@ impl From for bool { } } -/// Enable channels. +/// Channels #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Channels { Stereo, + /// Mono left Left, + /// Mono right Right, } @@ -235,8 +352,6 @@ impl From for u8 { } /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. pub struct I2S<'d, T: Instance> { _p: PeripheralRef<'d, T>, } @@ -278,29 +393,32 @@ impl<'d, T: Instance> I2S<'d, T> { ) -> Self { into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout); - let r = T::regs(); - Self::apply_config(&r.config, &config); - Self::select_pins(&r.psel, mck, sck, lrck, sdin, sdout); - Self::setup_interrupt(irq, r); + Self::apply_config(&config); + Self::select_pins(mck, sck, lrck, sdin, sdout); + Self::setup_interrupt(irq); - r.enable.write(|w| w.enable().enabled()); + T::regs().enable.write(|w| w.enable().enabled()); Self { _p: i2s } } + /// I2S output only pub fn output(self) -> Output<'d, T> { Output { _p: self._p } } + /// I2S input only pub fn input(self) -> Input<'d, T> { Input { _p: self._p } } + /// I2S full duplex (input and output) pub fn full_duplex(self) -> FullDuplex<'d, T> { FullDuplex { _p: self._p } } - fn apply_config(c: &CONFIG, config: &Config) { + fn apply_config(config: &Config) { + let c = &T::regs().config; match config.mode { Mode::Master { freq, ratio } => { c.mode.write(|w| w.mode().master()); @@ -322,13 +440,14 @@ impl<'d, T: Instance> I2S<'d, T> { } fn select_pins( - psel: &PSEL, mck: PeripheralRef<'d, AnyPin>, sck: PeripheralRef<'d, AnyPin>, lrck: PeripheralRef<'d, AnyPin>, sdin: PeripheralRef<'d, AnyPin>, sdout: PeripheralRef<'d, AnyPin>, ) { + let psel = &T::regs().psel; + psel.mck.write(|w| { unsafe { w.bits(mck.psel_bits()) }; w.connect().connected() @@ -355,21 +474,23 @@ impl<'d, T: Instance> I2S<'d, T> { }); } - fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) { + fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>) { irq.set_handler(Self::on_interrupt); - // irq.set_priority(Priority::P1); // TODO review priorities irq.unpend(); irq.enable(); let device = Device::::new(); device.disable_tx_ptr_interrupt(); device.disable_rx_ptr_interrupt(); + device.disable_stopped_interrupt(); device.reset_tx_ptr_event(); device.reset_rx_ptr_event(); + device.reset_stopped_event(); device.enable_tx_ptr_interrupt(); device.enable_rx_ptr_interrupt(); + device.enable_stopped_interrupt(); } fn on_interrupt(_: *mut ()) { @@ -387,41 +508,40 @@ impl<'d, T: Instance> I2S<'d, T> { s.rx_waker.wake(); device.disable_rx_ptr_interrupt(); } + + if device.is_stopped() { + trace!("STOPPED INT"); + s.stop_waker.wake(); + device.disable_stopped_interrupt(); + } } -} -pub struct Output<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance> Output<'d, T> { - /// Starts I2S transfer. - #[inline(always)] - pub fn start(&self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - // TODO what to do if it is started already? + async fn stop() { + compiler_fence(Ordering::SeqCst); let device = Device::::new(); - device.enable(); - device.set_tx_buffer(buffer)?; - device.enable_tx(); - device.start(); + device.stop(); - Ok(()) + T::state().started.store(false, Ordering::Relaxed); + + poll_fn(|cx| { + T::state().stop_waker.register(cx.waker()); + + if device.is_stopped() { + trace!("STOP: Ready"); + device.reset_stopped_event(); + Poll::Ready(()) + } else { + trace!("STOP: Pending"); + Poll::Pending + } + }) + .await; + + device.disable(); } - /// Stops the I2S transfer and waits until it has stopped. - #[inline(always)] - pub async fn stop(&self) { - todo!() - } - - /// Transmits the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - #[allow(unused_mut)] - pub async fn send(&mut self, buffer: B) -> Result<(), Error> + async fn send(buffer: B) -> Result<(), Error> where B: Buffer, { @@ -454,24 +574,191 @@ impl<'d, T: Instance> Output<'d, T> { Ok(()) } + + async fn receive(buffer: B) -> Result<(), Error> + where + B: Buffer, + { + trace!("RECEIVE: {}", buffer.bytes_ptr() as u32); + + let device = Device::::new(); + let drop = device.on_rx_drop(); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + T::state().rx_waker.register(cx.waker()); + + if device.is_rx_ptr_updated() { + trace!("RX POLL: Ready"); + device.reset_rx_ptr_event(); + device.enable_rx_ptr_interrupt(); + Poll::Ready(()) + } else { + trace!("RX POLL: Pending"); + Poll::Pending + } + }) + .await; + + device.set_rx_buffer(buffer)?; + + compiler_fence(Ordering::SeqCst); + drop.defuse(); + + Ok(()) + } } +/// I2S output +pub struct Output<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Output<'d, T> { + /// Prepare the initial buffer and start the I2S transfer. + pub async fn start(&self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_tx(); + device.set_tx_buffer(buffer)?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the given `buffer` for transmission in the DMA. + /// Buffer address must be 4 byte aligned and located in RAM. + /// The buffer must not be written while being used by the DMA, + /// which takes two other `send`s being awaited. + #[allow(unused_mut)] + pub async fn send(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + I2S::::send(buffer).await + } +} + +/// I2S input pub struct Input<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> Input<'d, T> { - // TODO + /// Prepare the initial buffer and start the I2S transfer. + pub async fn start(&self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_rx(); + device.set_rx_buffer(buffer)?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the given `buffer` for reception from the DMA. + /// Buffer address must be 4 byte aligned and located in RAM. + /// The buffer must not be read while being used by the DMA, + /// which takes two other `receive`s being awaited. + #[allow(unused_mut)] + pub async fn receive(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + I2S::::receive(buffer).await + } } +/// I2S ful duplex (input & output) pub struct FullDuplex<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> FullDuplex<'d, T> { - // TODO + /// Prepare the initial buffers and start the I2S transfer. + pub async fn start(&self, buffer_out: B, buffer_in: B) -> Result<(), Error> + where + B: Buffer, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_tx(); + device.enable_rx(); + device.set_tx_buffer(buffer_out)?; + device.set_rx_buffer(buffer_in)?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the given `buffer_out` and `buffer_in` for transmission/reception from the DMA. + /// Buffer address must be 4 byte aligned and located in RAM. + /// The buffers must not be written/read while being used by the DMA, + /// which takes two other `send_and_receive` operations being awaited. + #[allow(unused_mut)] + pub async fn send_and_receive(&mut self, buffer_out: B, buffer_in: B) -> Result<(), Error> + where + B: Buffer, + { + I2S::::send(buffer_out).await?; + I2S::::receive(buffer_in).await?; + Ok(()) + } } +/// Helper encapsulating common I2S device operations. struct Device(&'static RegisterBlock, PhantomData); impl Device { @@ -521,6 +808,34 @@ impl Device { self.0.tasks_start.write(|w| unsafe { w.bits(1) }); } + #[inline(always)] + fn stop(&self) { + self.0.tasks_stop.write(|w| unsafe { w.bits(1) }); + } + + #[inline(always)] + fn is_stopped(&self) -> bool { + self.0.events_stopped.read().bits() != 0 + } + + #[inline(always)] + fn reset_stopped_event(&self) { + trace!("STOPPED EVENT: Reset"); + self.0.events_stopped.reset(); + } + + #[inline(always)] + fn disable_stopped_interrupt(&self) { + trace!("STOPPED INTERRUPT: Disabled"); + self.0.intenclr.write(|w| w.stopped().clear()); + } + + #[inline(always)] + fn enable_stopped_interrupt(&self) { + trace!("STOPPED INTERRUPT: Enabled"); + self.0.intenset.write(|w| w.stopped().set()); + } + #[inline] fn set_tx_buffer(&self, buffer: B) -> Result<(), Error> where @@ -606,6 +921,23 @@ impl Device { }) } + #[inline] + fn on_rx_drop(&self) -> OnDrop { + OnDrop::new(move || { + trace!("RX DROP: Stopping"); + + let device = Device::::new(); + device.disable_rx_ptr_interrupt(); + device.reset_rx_ptr_event(); + device.disable_rx(); + + // TX is stopped almost instantly, spinning is fine. + while !device.is_rx_ptr_updated() {} + + trace!("RX DROP: Stopped"); + }) + } + fn validate_buffer(buffer: B) -> Result<(u32, u32), Error> where B: Buffer, @@ -632,6 +964,56 @@ impl Device { } } +/// Sample details +pub trait Sample: Sized + Copy + Default { + const WIDTH: usize; + const SCALE: Self; +} + +impl Sample for i8 { + const WIDTH: usize = 8; + const SCALE: Self = 1 << (Self::WIDTH - 1); +} + +impl Sample for i16 { + const WIDTH: usize = 16; + const SCALE: Self = 1 << (Self::WIDTH - 1); +} + +impl Sample for i32 { + const WIDTH: usize = 24; + const SCALE: Self = 1 << (Self::WIDTH - 1); +} + +/// A 4-bytes aligned [Buffer]. +#[repr(align(4))] +pub struct AlignedBuffer([T; N]); + +impl AlignedBuffer { + pub fn new(array: [T; N]) -> Self { + Self(array) + } +} + +impl Default for AlignedBuffer { + fn default() -> Self { + Self([T::default(); N]) + } +} + +impl AsRef<[T]> for AlignedBuffer { + fn as_ref(&self) -> &[T] { + self.0.as_slice() + } +} + +impl AsMut<[T]> for AlignedBuffer { + fn as_mut(&mut self) -> &mut [T] { + self.0.as_mut_slice() + } +} + +/// Common operations required for a buffer to be used by the DMA pub trait Buffer: Sized { fn bytes_ptr(&self) -> *const u8; fn bytes_len(&self) -> usize; @@ -674,22 +1056,25 @@ impl Buffer for &[i32] { } pub(crate) mod sealed { - use core::sync::atomic::AtomicI32; + use core::sync::atomic::AtomicBool; use embassy_sync::waitqueue::AtomicWaker; - use super::*; - + /// Peripheral static state pub struct State { + pub started: AtomicBool, pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, + pub stop_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { + started: AtomicBool::new(false), rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), + stop_waker: AtomicWaker::new(), } } } @@ -704,8 +1089,6 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { type Interrupt: Interrupt; } -// TODO: Unsure why this macro is flagged as unused by CI when in fact it's used elsewhere? -#[allow(unused_macros)] macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::sealed::Instance for peripherals::$type { diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s-generate.rs similarity index 62% rename from examples/nrf/src/bin/i2s.rs rename to examples/nrf/src/bin/i2s-generate.rs index 9b3144f2..f59b63ce 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s-generate.rs @@ -4,59 +4,41 @@ use core::f32::consts::PI; -use defmt::{error, info, trace}; +use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000}; -use embassy_nrf::pac::ficr::info; -use embassy_nrf::{i2s, interrupt}; +use embassy_nrf::i2s::{self, Sample as _}; +use embassy_nrf::interrupt; use {defmt_rtt as _, panic_probe as _}; -#[repr(align(4))] -pub struct AlignedBuffer(T); - -impl AsRef for AlignedBuffer { - fn as_ref(&self) -> &T { - &self.0 - } -} - -impl AsMut for AlignedBuffer { - fn as_mut(&mut self) -> &mut T { - &mut self.0 - } -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); + let mut config = i2s::Config::default(); - config.mode = MODE_MASTER_32000; - // config.mode = Mode::Master { - // freq: MckFreq::_32MDiv10, - // ratio: Ratio::_256x, - // }; // 12500 Hz - config.channels = Channels::Left; - config.swidth = SampleWidth::_16bit; + config.mode = i2s::ExactSampleRate::_50000.into(); + config.channels = i2s::Channels::Left; + config.swidth = i2s::SampleWidth::_16bit; let sample_rate = config.mode.sample_rate().expect("I2S Master"); let inv_sample_rate = 1.0 / sample_rate as f32; info!("Sample rate: {}", sample_rate); // Wait for a button press + // use embassy_nrf::gpio::{Input, Pin, Pull}; // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up); // btn1.wait_for_low().await; let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output(); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config) + .output(); type Sample = i16; - const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample; - const NUM_SAMPLES: usize = 2000; - let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [ - AlignedBuffer([0; NUM_SAMPLES]), - AlignedBuffer([0; NUM_SAMPLES]), - AlignedBuffer([0; NUM_SAMPLES]), + const NUM_SAMPLES: usize = 6000; + + let mut buffers: [i2s::AlignedBuffer; 3] = [ + i2s::AlignedBuffer::default(), + i2s::AlignedBuffer::default(), + i2s::AlignedBuffer::default(), ]; let mut carrier = SineOsc::new(); @@ -66,32 +48,29 @@ async fn main(_spawner: Spawner) { freq_mod.set_amplitude(1.0); let mut amp_mod = SineOsc::new(); - amp_mod.set_frequency(4.0, inv_sample_rate); + amp_mod.set_frequency(16.0, inv_sample_rate); amp_mod.set_amplitude(0.5); let mut generate = |buf: &mut [Sample]| { - let ptr = buf as *const [Sample] as *const Sample as u32; - trace!("GEN: {}", ptr); - - for sample in &mut buf.as_mut().chunks_mut(1) { - let signal = carrier.generate(); + for sample in &mut buf.chunks_mut(1) { let freq_modulation = bipolar_to_unipolar(freq_mod.generate()); - carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate); + carrier.set_frequency(220.0 + 440.0 * freq_modulation, inv_sample_rate); let amp_modulation = bipolar_to_unipolar(amp_mod.generate()); carrier.set_amplitude(amp_modulation); - let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample; + let signal = carrier.generate(); + let value = (Sample::SCALE as f32 * signal) as Sample; sample[0] = value; } }; - generate(buffers[0].as_mut().as_mut_slice()); - generate(buffers[1].as_mut().as_mut_slice()); + generate(buffers[0].as_mut()); + generate(buffers[1].as_mut()); - i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start"); + i2s.start(buffers[0].as_ref()).await.expect("I2S Start"); let mut index = 1; loop { - if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await { + if let Err(err) = i2s.send(buffers[index].as_ref()).await { error!("{}", err); } @@ -99,7 +78,7 @@ async fn main(_spawner: Spawner) { if index >= 3 { index = 0; } - generate(buffers[index].as_mut().as_mut_slice()); + generate(buffers[index].as_mut()); } } From 16838f8a66d8a3b74f0fe1ab9124532226e7166a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 00:32:09 +0100 Subject: [PATCH 142/374] Fix format --- examples/nrf/src/bin/i2s-generate.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/nrf/src/bin/i2s-generate.rs b/examples/nrf/src/bin/i2s-generate.rs index f59b63ce..c2b5578f 100644 --- a/examples/nrf/src/bin/i2s-generate.rs +++ b/examples/nrf/src/bin/i2s-generate.rs @@ -29,8 +29,7 @@ async fn main(_spawner: Spawner) { // btn1.wait_for_low().await; let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config) - .output(); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config).output(); type Sample = i16; const NUM_SAMPLES: usize = 6000; From 64e8cfef8e53293b35d2c5ea2b822bdc3a12111e Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 01:38:03 +0100 Subject: [PATCH 143/374] Fix build --- embassy-nrf/src/chips/nrf52832.rs | 7 ++++++- embassy-nrf/src/chips/nrf52833.rs | 7 ++++++- embassy-nrf/src/chips/nrf52840.rs | 2 +- embassy-nrf/src/i2s.rs | 7 ++----- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 81e66c19..a0aaba9d 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -138,6 +138,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // I2S + I2S, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -234,6 +237,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_i2s!(I2S, I2S, I2S); + pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; @@ -274,6 +279,6 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); + declare!(I2S); } diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 92499e3c..9063f486 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { // PDM PDM, + + // I2S + I2S, } #[cfg(feature = "nightly")] @@ -280,6 +283,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_i2s!(I2S, I2S, I2S); + pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; @@ -320,10 +325,10 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); declare!(USBD); declare!(UARTE1); declare!(PWM3); declare!(SPIM3); + declare!(I2S); } diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index cf800c7b..4047ee0a 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -330,7 +330,6 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); declare!(USBD); declare!(UARTE1); @@ -338,4 +337,5 @@ pub mod irqs { declare!(CRYPTOCELL); declare!(PWM3); declare!(SPIM3); + declare!(I2S); } diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 53d9f9a1..52b72df2 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,13 +14,10 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; use crate::pac::i2s::RegisterBlock; -use crate::Peripheral; +use crate::{EASY_DMA_SIZE, Peripheral}; // TODO: Define those in lib.rs somewhere else -/// I2S EasyDMA MAXCNT bit length = 14 -const MAX_DMA_MAXCNT: u32 = 1 << 14; - /// Limits for Easy DMA - it can only read from data ram pub const SRAM_LOWER: usize = 0x2000_0000; pub const SRAM_UPPER: usize = 0x3000_0000; @@ -956,7 +953,7 @@ impl Device { Err(Error::BufferLengthMisaligned) } else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { Err(Error::BufferNotInDataMemory) - } else if maxcnt > MAX_DMA_MAXCNT { + } else if maxcnt as usize > EASY_DMA_SIZE { Err(Error::BufferTooLong) } else { Ok((ptr, maxcnt)) From f5391efe22374e0c55005f20c6a644bda3acd50c Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 02:17:58 +0100 Subject: [PATCH 144/374] Fix fmt --- embassy-nrf/src/i2s.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 52b72df2..11c09a22 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,7 +14,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; use crate::pac::i2s::RegisterBlock; -use crate::{EASY_DMA_SIZE, Peripheral}; +use crate::{Peripheral, EASY_DMA_SIZE}; // TODO: Define those in lib.rs somewhere else From 15a93246d6bb3e0bea268ff919bca073a9890247 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:18:20 +0100 Subject: [PATCH 145/374] Buffer management in line with other peripherals. Constructor and config redesign --- embassy-nrf/src/i2s.rs | 721 ++++++++---------- .../bin/{i2s-generate.rs => i2s_waveform.rs} | 103 ++- 2 files changed, 395 insertions(+), 429 deletions(-) rename examples/nrf/src/bin/{i2s-generate.rs => i2s_waveform.rs} (50%) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 11c09a22..d5815160 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -4,6 +4,8 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::mem::size_of; +use core::ops::{Deref, DerefMut}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -14,14 +16,9 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; use crate::pac::i2s::RegisterBlock; +use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; -// TODO: Define those in lib.rs somewhere else - -/// Limits for Easy DMA - it can only read from data ram -pub const SRAM_LOWER: usize = 0x2000_0000; -pub const SRAM_UPPER: usize = 0x3000_0000; - #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -33,159 +30,42 @@ pub enum Error { BufferLengthMisaligned, } -/// Approximate sample rates. -/// -/// Those are common sample rates that can not be configured without an small error. -/// -/// For custom master clock configuration, please refer to [Mode]. -#[derive(Clone, Copy)] -pub enum ApproxSampleRate { - _11025, - _16000, - _22050, - _32000, - _44100, - _48000, -} - -impl From for Mode { - fn from(value: ApproxSampleRate) -> Self { - match value { - // error = 86 - ApproxSampleRate::_11025 => Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_192x, - }, - // error = 127 - ApproxSampleRate::_16000 => Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_96x, - }, - // error = 172 - ApproxSampleRate::_22050 => Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_96x, - }, - // error = 254 - ApproxSampleRate::_32000 => Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_48x, - }, - // error = 344 - ApproxSampleRate::_44100 => Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_48x, - }, - // error = 381 - ApproxSampleRate::_48000 => Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_32x, - }, - } - } -} - -impl ApproxSampleRate { - pub fn sample_rate(&self) -> u32 { - // This will always provide a Master mode, so it is safe to unwrap. - Mode::from(*self).sample_rate().unwrap() - } -} - -/// Exact sample rates. -/// -/// Those are non standard sample rates that can be configured without error. -/// -/// For custom master clock configuration, please refer to [Mode]. -#[derive(Clone, Copy)] -pub enum ExactSampleRate { - _8000, - _10582, - _12500, - _15625, - _15873, - _25000, - _31250, - _50000, - _62500, - _100000, - _125000, -} - -impl ExactSampleRate { - pub fn sample_rate(&self) -> u32 { - // This will always provide a Master mode, so it is safe to unwrap. - Mode::from(*self).sample_rate().unwrap() - } -} - -impl From for Mode { - fn from(value: ExactSampleRate) -> Self { - match value { - ExactSampleRate::_8000 => Mode::Master { - freq: MckFreq::_32MDiv125, - ratio: Ratio::_32x, - }, - ExactSampleRate::_10582 => Mode::Master { - freq: MckFreq::_32MDiv63, - ratio: Ratio::_48x, - }, - ExactSampleRate::_12500 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_256x, - }, - ExactSampleRate::_15625 => Mode::Master { - freq: MckFreq::_32MDiv32, - ratio: Ratio::_64x, - }, - ExactSampleRate::_15873 => Mode::Master { - freq: MckFreq::_32MDiv63, - ratio: Ratio::_32x, - }, - ExactSampleRate::_25000 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_128x, - }, - ExactSampleRate::_31250 => Mode::Master { - freq: MckFreq::_32MDiv32, - ratio: Ratio::_32x, - }, - ExactSampleRate::_50000 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_64x, - }, - ExactSampleRate::_62500 => Mode::Master { - freq: MckFreq::_32MDiv16, - ratio: Ratio::_32x, - }, - ExactSampleRate::_100000 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_32x, - }, - ExactSampleRate::_125000 => Mode::Master { - freq: MckFreq::_32MDiv8, - ratio: Ratio::_32x, - }, - } - } -} - /// I2S configuration. #[derive(Clone)] #[non_exhaustive] pub struct Config { - pub mode: Mode, - pub swidth: SampleWidth, + pub sample_width: SampleWidth, pub align: Align, pub format: Format, pub channels: Channels, } +impl Config { + pub fn sample_width(mut self, sample_width: SampleWidth) -> Self { + self.sample_width = sample_width; + self + } + + pub fn align(mut self, align: Align) -> Self { + self.align = align; + self + } + + pub fn format(mut self, format: Format) -> Self { + self.format = format; + self + } + + pub fn channels(mut self, channels: Channels) -> Self { + self.channels = channels; + self + } +} + impl Default for Config { fn default() -> Self { Self { - mode: ExactSampleRate::_31250.into(), - swidth: SampleWidth::_16bit, + sample_width: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, channels: Channels::Stereo, @@ -195,17 +75,20 @@ impl Default for Config { /// I2S Mode #[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum Mode { - Master { freq: MckFreq, ratio: Ratio }, - Slave, +pub struct MasterClock { + freq: MckFreq, + ratio: Ratio, } -impl Mode { - pub fn sample_rate(&self) -> Option { - match self { - Mode::Master { freq, ratio } => Some(freq.to_frequency() / ratio.to_divisor()), - Mode::Slave => None, - } +impl MasterClock { + pub fn new(freq: MckFreq, ratio: Ratio) -> Self { + Self { freq, ratio } + } +} + +impl MasterClock { + pub fn sample_rate(&self) -> u32 { + self.freq.to_frequency() / self.ratio.to_divisor() } } @@ -275,17 +158,106 @@ pub enum Ratio { impl Ratio { const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; + /// Return the value that needs to be written to the register. + pub fn to_register_value(&self) -> u8 { + usize::from(*self) as u8 + } + pub fn to_divisor(&self) -> u32 { - Self::RATIOS[u8::from(*self) as usize] + Self::RATIOS[usize::from(*self)] } } -impl From for u8 { +impl From for usize { fn from(variant: Ratio) -> Self { variant as _ } } +/// Approximate sample rates. +/// +/// Those are common sample rates that can not be configured without an small error. +/// +/// For custom master clock configuration, please refer to [MasterClock]. +#[derive(Clone, Copy)] +pub enum ApproxSampleRate { + _11025, + _16000, + _22050, + _32000, + _44100, + _48000, +} + +impl From for MasterClock { + fn from(value: ApproxSampleRate) -> Self { + match value { + // error = 86 + ApproxSampleRate::_11025 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_192x), + // error = 127 + ApproxSampleRate::_16000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_96x), + // error = 172 + ApproxSampleRate::_22050 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_96x), + // error = 254 + ApproxSampleRate::_32000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_48x), + // error = 344 + ApproxSampleRate::_44100 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_48x), + // error = 381 + ApproxSampleRate::_48000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_32x), + } + } +} + +impl ApproxSampleRate { + pub fn sample_rate(&self) -> u32 { + MasterClock::from(*self).sample_rate() + } +} + +/// Exact sample rates. +/// +/// Those are non standard sample rates that can be configured without error. +/// +/// For custom master clock configuration, please refer to [Mode]. +#[derive(Clone, Copy)] +pub enum ExactSampleRate { + _8000, + _10582, + _12500, + _15625, + _15873, + _25000, + _31250, + _50000, + _62500, + _100000, + _125000, +} + +impl ExactSampleRate { + pub fn sample_rate(&self) -> u32 { + MasterClock::from(*self).sample_rate() + } +} + +impl From for MasterClock { + fn from(value: ExactSampleRate) -> Self { + match value { + ExactSampleRate::_8000 => MasterClock::new(MckFreq::_32MDiv125, Ratio::_32x), + ExactSampleRate::_10582 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_48x), + ExactSampleRate::_12500 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_256x), + ExactSampleRate::_15625 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_64x), + ExactSampleRate::_15873 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_32x), + ExactSampleRate::_25000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_128x), + ExactSampleRate::_31250 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_32x), + ExactSampleRate::_50000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_64x), + ExactSampleRate::_62500 => MasterClock::new(MckFreq::_32MDiv16, Ratio::_32x), + ExactSampleRate::_100000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_32x), + ExactSampleRate::_125000 => MasterClock::new(MckFreq::_32MDiv8, Ratio::_32x), + } + } +} + /// Sample width. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SampleWidth { @@ -336,10 +308,8 @@ impl From for bool { #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Channels { Stereo, - /// Mono left - Left, - /// Mono right - Right, + MonoLeft, + MonoRight, } impl From for u8 { @@ -350,131 +320,160 @@ impl From for u8 { /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. pub struct I2S<'d, T: Instance> { - _p: PeripheralRef<'d, T>, + i2s: PeripheralRef<'d, T>, + irq: PeripheralRef<'d, T::Interrupt>, + mck: Option>, + sck: PeripheralRef<'d, AnyPin>, + lrck: PeripheralRef<'d, AnyPin>, + sdin: Option>, + sdout: Option>, + master_clock: Option, + config: Config, } impl<'d, T: Instance> I2S<'d, T> { - /// Create a new I2S - pub fn new( + /// Create a new I2S in master mode + pub fn master( i2s: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, mck: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, - sdin: impl Peripheral

+ 'd, - sdout: impl Peripheral

+ 'd, + master_clock: MasterClock, config: Config, ) -> Self { - into_ref!(mck, sck, lrck, sdin, sdout); - Self::new_inner( + into_ref!(i2s, irq, mck, sck, lrck); + Self { i2s, irq, - mck.map_into(), - sck.map_into(), - lrck.map_into(), - sdin.map_into(), - sdout.map_into(), + mck: Some(mck.map_into()), + sck: sck.map_into(), + lrck: lrck.map_into(), + sdin: None, + sdout: None, + master_clock: Some(master_clock), config, - ) + } } - fn new_inner( + /// Create a new I2S in slave mode + pub fn slave( i2s: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, - mck: PeripheralRef<'d, AnyPin>, - sck: PeripheralRef<'d, AnyPin>, - lrck: PeripheralRef<'d, AnyPin>, - sdin: PeripheralRef<'d, AnyPin>, - sdout: PeripheralRef<'d, AnyPin>, + sck: impl Peripheral

+ 'd, + lrck: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout); - - Self::apply_config(&config); - Self::select_pins(mck, sck, lrck, sdin, sdout); - Self::setup_interrupt(irq); - - T::regs().enable.write(|w| w.enable().enabled()); - - Self { _p: i2s } + into_ref!(i2s, irq, sck, lrck); + Self { + i2s, + irq, + mck: None, + sck: sck.map_into(), + lrck: lrck.map_into(), + sdin: None, + sdout: None, + master_clock: None, + config, + } } /// I2S output only - pub fn output(self) -> Output<'d, T> { - Output { _p: self._p } + pub fn output(mut self, sdout: impl Peripheral

+ 'd) -> OutputStream<'d, T> { + self.sdout = Some(sdout.into_ref().map_into()); + OutputStream { _p: self.build() } } /// I2S input only - pub fn input(self) -> Input<'d, T> { - Input { _p: self._p } + pub fn input(mut self, sdin: impl Peripheral

+ 'd) -> InputStream<'d, T> { + self.sdin = Some(sdin.into_ref().map_into()); + InputStream { _p: self.build() } } /// I2S full duplex (input and output) - pub fn full_duplex(self) -> FullDuplex<'d, T> { - FullDuplex { _p: self._p } + pub fn full_duplex( + mut self, + sdin: impl Peripheral

+ 'd, + sdout: impl Peripheral

+ 'd, + ) -> FullDuplexStream<'d, T> { + self.sdout = Some(sdout.into_ref().map_into()); + self.sdin = Some(sdin.into_ref().map_into()); + FullDuplexStream { _p: self.build() } } - fn apply_config(config: &Config) { + fn build(self) -> PeripheralRef<'d, T> { + self.apply_config(); + self.select_pins(); + self.setup_interrupt(); + + let device = Device::::new(); + device.enable(); + + self.i2s + } + + fn apply_config(&self) { let c = &T::regs().config; - match config.mode { - Mode::Master { freq, ratio } => { + match &self.master_clock { + Some(MasterClock { freq, ratio }) => { c.mode.write(|w| w.mode().master()); c.mcken.write(|w| w.mcken().enabled()); c.mckfreq .write(|w| unsafe { w.mckfreq().bits(freq.to_register_value()) }); - c.ratio.write(|w| unsafe { w.ratio().bits(ratio.into()) }); + c.ratio.write(|w| unsafe { w.ratio().bits(ratio.to_register_value()) }); } - Mode::Slave => { + None => { c.mode.write(|w| w.mode().slave()); } }; - c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) }); - c.align.write(|w| w.align().bit(config.align.into())); - c.format.write(|w| w.format().bit(config.format.into())); + c.swidth + .write(|w| unsafe { w.swidth().bits(self.config.sample_width.into()) }); + c.align.write(|w| w.align().bit(self.config.align.into())); + c.format.write(|w| w.format().bit(self.config.format.into())); c.channels - .write(|w| unsafe { w.channels().bits(config.channels.into()) }); + .write(|w| unsafe { w.channels().bits(self.config.channels.into()) }); } - fn select_pins( - mck: PeripheralRef<'d, AnyPin>, - sck: PeripheralRef<'d, AnyPin>, - lrck: PeripheralRef<'d, AnyPin>, - sdin: PeripheralRef<'d, AnyPin>, - sdout: PeripheralRef<'d, AnyPin>, - ) { + fn select_pins(&self) { let psel = &T::regs().psel; - psel.mck.write(|w| { - unsafe { w.bits(mck.psel_bits()) }; - w.connect().connected() - }); + if let Some(mck) = &self.mck { + psel.mck.write(|w| { + unsafe { w.bits(mck.psel_bits()) }; + w.connect().connected() + }); + } psel.sck.write(|w| { - unsafe { w.bits(sck.psel_bits()) }; + unsafe { w.bits(self.sck.psel_bits()) }; w.connect().connected() }); psel.lrck.write(|w| { - unsafe { w.bits(lrck.psel_bits()) }; + unsafe { w.bits(self.lrck.psel_bits()) }; w.connect().connected() }); - psel.sdin.write(|w| { - unsafe { w.bits(sdin.psel_bits()) }; - w.connect().connected() - }); + if let Some(sdin) = &self.sdin { + psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + } - psel.sdout.write(|w| { - unsafe { w.bits(sdout.psel_bits()) }; - w.connect().connected() - }); + if let Some(sdout) = &self.sdout { + psel.sdout.write(|w| { + unsafe { w.bits(sdout.psel_bits()) }; + w.connect().connected() + }); + } } - fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>) { - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + fn setup_interrupt(&self) { + self.irq.set_handler(Self::on_interrupt); + self.irq.unpend(); + self.irq.enable(); let device = Device::::new(); device.disable_tx_ptr_interrupt(); @@ -538,17 +537,32 @@ impl<'d, T: Instance> I2S<'d, T> { device.disable(); } - async fn send(buffer: B) -> Result<(), Error> + async fn send_from_ram(buffer_ptr: *const [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - trace!("SEND: {}", buffer.bytes_ptr() as u32); + trace!("SEND: {}", buffer_ptr as *const S as u32); - let device = Device::::new(); - let drop = device.on_tx_drop(); + slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; compiler_fence(Ordering::SeqCst); + let device = Device::::new(); + + let drop = OnDrop::new(move || { + trace!("TX DROP: Stopping"); + + let device = Device::::new(); + device.disable_tx_ptr_interrupt(); + device.reset_tx_ptr_event(); + device.disable_tx(); + + // TX is stopped almost instantly, spinning is fine. + while !device.is_tx_ptr_updated() {} + + trace!("TX DROP: Stopped"); + }); + poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); @@ -564,7 +578,7 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.set_tx_buffer(buffer)?; + device.update_tx(buffer_ptr)?; compiler_fence(Ordering::SeqCst); drop.defuse(); @@ -572,17 +586,33 @@ impl<'d, T: Instance> I2S<'d, T> { Ok(()) } - async fn receive(buffer: B) -> Result<(), Error> + async fn receive_from_ram(buffer_ptr: *mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - trace!("RECEIVE: {}", buffer.bytes_ptr() as u32); + trace!("RECEIVE: {}", buffer_ptr as *const S as u32); - let device = Device::::new(); - let drop = device.on_rx_drop(); + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. compiler_fence(Ordering::SeqCst); + let device = Device::::new(); + + let drop = OnDrop::new(move || { + trace!("RX DROP: Stopping"); + + let device = Device::::new(); + device.disable_rx_ptr_interrupt(); + device.reset_rx_ptr_event(); + device.disable_rx(); + + // TX is stopped almost instantly, spinning is fine. + while !device.is_rx_ptr_updated() {} + + trace!("RX DROP: Stopped"); + }); + poll_fn(|cx| { T::state().rx_waker.register(cx.waker()); @@ -598,9 +628,10 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.set_rx_buffer(buffer)?; + device.update_rx(buffer_ptr)?; compiler_fence(Ordering::SeqCst); + drop.defuse(); Ok(()) @@ -608,15 +639,15 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output -pub struct Output<'d, T: Instance> { +pub struct OutputStream<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> Output<'d, T> { +impl<'d, T: Instance> OutputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: B) -> Result<(), Error> + pub async fn start(&self, buffer: &[S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { let device = Device::::new(); @@ -627,7 +658,8 @@ impl<'d, T: Instance> Output<'d, T> { device.enable(); device.enable_tx(); - device.set_tx_buffer(buffer)?; + + device.update_tx(buffer as *const [S])?; s.started.store(true, Ordering::Relaxed); @@ -647,24 +679,24 @@ impl<'d, T: Instance> Output<'d, T> { /// The buffer must not be written while being used by the DMA, /// which takes two other `send`s being awaited. #[allow(unused_mut)] - pub async fn send(&mut self, buffer: B) -> Result<(), Error> + pub async fn send_from_ram(&mut self, buffer: &[S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - I2S::::send(buffer).await + I2S::::send_from_ram(buffer as *const [S]).await } } /// I2S input -pub struct Input<'d, T: Instance> { +pub struct InputStream<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> Input<'d, T> { +impl<'d, T: Instance> InputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: B) -> Result<(), Error> + pub async fn start(&self, buffer: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { let device = Device::::new(); @@ -675,7 +707,8 @@ impl<'d, T: Instance> Input<'d, T> { device.enable(); device.enable_rx(); - device.set_rx_buffer(buffer)?; + + device.update_rx(buffer as *mut [S])?; s.started.store(true, Ordering::Relaxed); @@ -695,24 +728,24 @@ impl<'d, T: Instance> Input<'d, T> { /// The buffer must not be read while being used by the DMA, /// which takes two other `receive`s being awaited. #[allow(unused_mut)] - pub async fn receive(&mut self, buffer: B) -> Result<(), Error> + pub async fn receive_from_ram(&mut self, buffer: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - I2S::::receive(buffer).await + I2S::::receive_from_ram(buffer as *mut [S]).await } } -/// I2S ful duplex (input & output) -pub struct FullDuplex<'d, T: Instance> { +/// I2S full duplex stream (input & output) +pub struct FullDuplexStream<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> FullDuplex<'d, T> { +impl<'d, T: Instance> FullDuplexStream<'d, T> { /// Prepare the initial buffers and start the I2S transfer. - pub async fn start(&self, buffer_out: B, buffer_in: B) -> Result<(), Error> + pub async fn start(&self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { let device = Device::::new(); @@ -724,8 +757,9 @@ impl<'d, T: Instance> FullDuplex<'d, T> { device.enable(); device.enable_tx(); device.enable_rx(); - device.set_tx_buffer(buffer_out)?; - device.set_rx_buffer(buffer_in)?; + + device.update_tx(buffer_out as *const [S])?; + device.update_rx(buffer_in as *mut [S])?; s.started.store(true, Ordering::Relaxed); @@ -745,12 +779,12 @@ impl<'d, T: Instance> FullDuplex<'d, T> { /// The buffers must not be written/read while being used by the DMA, /// which takes two other `send_and_receive` operations being awaited. #[allow(unused_mut)] - pub async fn send_and_receive(&mut self, buffer_out: B, buffer_in: B) -> Result<(), Error> + pub async fn send_and_receive_from_ram(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - I2S::::send(buffer_out).await?; - I2S::::receive(buffer_in).await?; + I2S::::send_from_ram(buffer_out as *const [S]).await?; + I2S::::receive_from_ram(buffer_in as *mut [S]).await?; Ok(()) } } @@ -833,38 +867,6 @@ impl Device { self.0.intenset.write(|w| w.stopped().set()); } - #[inline] - fn set_tx_buffer(&self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - let (ptr, maxcnt) = Self::validate_buffer(buffer)?; - self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); - Ok(()) - } - - #[inline] - fn set_rx_buffer(&self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - let (ptr, maxcnt) = Self::validate_buffer(buffer)?; - self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); - Ok(()) - } - - #[inline(always)] - fn is_tx_ptr_updated(&self) -> bool { - self.0.events_txptrupd.read().bits() != 0 - } - - #[inline(always)] - fn is_rx_ptr_updated(&self) -> bool { - self.0.events_rxptrupd.read().bits() != 0 - } - #[inline(always)] fn reset_tx_ptr_event(&self) { trace!("TX PTR EVENT: Reset"); @@ -901,58 +903,44 @@ impl Device { self.0.intenclr.write(|w| w.rxptrupd().clear()); } - #[inline] - fn on_tx_drop(&self) -> OnDrop { - OnDrop::new(move || { - trace!("TX DROP: Stopping"); + #[inline(always)] + fn is_tx_ptr_updated(&self) -> bool { + self.0.events_txptrupd.read().bits() != 0 + } - let device = Device::::new(); - device.disable_tx_ptr_interrupt(); - device.reset_tx_ptr_event(); - device.disable_tx(); - - // TX is stopped almost instantly, spinning is fine. - while !device.is_tx_ptr_updated() {} - - trace!("TX DROP: Stopped"); - }) + #[inline(always)] + fn is_rx_ptr_updated(&self) -> bool { + self.0.events_rxptrupd.read().bits() != 0 } #[inline] - fn on_rx_drop(&self) -> OnDrop { - OnDrop::new(move || { - trace!("RX DROP: Stopping"); - - let device = Device::::new(); - device.disable_rx_ptr_interrupt(); - device.reset_rx_ptr_event(); - device.disable_rx(); - - // TX is stopped almost instantly, spinning is fine. - while !device.is_rx_ptr_updated() {} - - trace!("RX DROP: Stopped"); - }) + fn update_tx(&self, buffer_ptr: *const [S]) -> Result<(), Error> { + let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + Ok(()) } - fn validate_buffer(buffer: B) -> Result<(u32, u32), Error> - where - B: Buffer, - { - let ptr = buffer.bytes_ptr() as u32; - let len = buffer.bytes_len(); - let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + #[inline] + fn update_rx(&self, buffer_ptr: *const [S]) -> Result<(), Error> { + let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + Ok(()) + } + + fn validated_dma_parts(buffer_ptr: *const [S]) -> Result<(u32, u32), Error> { + let (ptr, len) = slice_ptr_parts(buffer_ptr); + let ptr = ptr as u32; + let bytes_len = len * size_of::(); + let maxcnt = (bytes_len / size_of::()) as u32; trace!("PTR={}, MAXCNT={}", ptr, maxcnt); - // TODO can we avoid repeating all those runtime checks for the same buffer again and again? - if ptr % 4 != 0 { Err(Error::BufferMisaligned) - } else if len % 4 != 0 { + } else if bytes_len % 4 != 0 { Err(Error::BufferLengthMisaligned) - } else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - Err(Error::BufferNotInDataMemory) } else if maxcnt as usize > EASY_DMA_SIZE { Err(Error::BufferTooLong) } else { @@ -998,60 +986,19 @@ impl Default for AlignedBuffer { } } -impl AsRef<[T]> for AlignedBuffer { - fn as_ref(&self) -> &[T] { +impl Deref for AlignedBuffer { + type Target = [T]; + fn deref(&self) -> &Self::Target { self.0.as_slice() } } -impl AsMut<[T]> for AlignedBuffer { - fn as_mut(&mut self) -> &mut [T] { +impl DerefMut for AlignedBuffer { + fn deref_mut(&mut self) -> &mut Self::Target { self.0.as_mut_slice() } } -/// Common operations required for a buffer to be used by the DMA -pub trait Buffer: Sized { - fn bytes_ptr(&self) -> *const u8; - fn bytes_len(&self) -> usize; -} - -impl Buffer for &[i8] { - #[inline] - fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() as *const u8 - } - - #[inline] - fn bytes_len(&self) -> usize { - self.len() - } -} - -impl Buffer for &[i16] { - #[inline] - fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() as *const u8 - } - - #[inline] - fn bytes_len(&self) -> usize { - self.len() * core::mem::size_of::() - } -} - -impl Buffer for &[i32] { - #[inline] - fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() as *const u8 - } - - #[inline] - fn bytes_len(&self) -> usize { - self.len() * core::mem::size_of::() - } -} - pub(crate) mod sealed { use core::sync::atomic::AtomicBool; diff --git a/examples/nrf/src/bin/i2s-generate.rs b/examples/nrf/src/bin/i2s_waveform.rs similarity index 50% rename from examples/nrf/src/bin/i2s-generate.rs rename to examples/nrf/src/bin/i2s_waveform.rs index c2b5578f..81858ff5 100644 --- a/examples/nrf/src/bin/i2s-generate.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -6,33 +6,29 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Sample as _}; +use embassy_nrf::i2s::{self, Channels, Config, MasterClock, Sample as _, SampleWidth, I2S}; use embassy_nrf::interrupt; use {defmt_rtt as _, panic_probe as _}; +type Sample = i16; + +const NUM_SAMPLES: usize = 6000; + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let mut config = i2s::Config::default(); - config.mode = i2s::ExactSampleRate::_50000.into(); - config.channels = i2s::Channels::Left; - config.swidth = i2s::SampleWidth::_16bit; - let sample_rate = config.mode.sample_rate().expect("I2S Master"); - let inv_sample_rate = 1.0 / sample_rate as f32; + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - // Wait for a button press - // use embassy_nrf::gpio::{Input, Pin, Pull}; - // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up); - // btn1.wait_for_low().await; + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config).output(); - - type Sample = i16; - const NUM_SAMPLES: usize = 6000; + let mut output_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28); let mut buffers: [i2s::AlignedBuffer; 3] = [ i2s::AlignedBuffer::default(), @@ -40,36 +36,16 @@ async fn main(_spawner: Spawner) { i2s::AlignedBuffer::default(), ]; - let mut carrier = SineOsc::new(); + let mut waveform = Waveform::new(1.0 / sample_rate as f32); - let mut freq_mod = SineOsc::new(); - freq_mod.set_frequency(8.0, inv_sample_rate); - freq_mod.set_amplitude(1.0); + waveform.process(&mut buffers[0]); + waveform.process(&mut buffers[1]); - let mut amp_mod = SineOsc::new(); - amp_mod.set_frequency(16.0, inv_sample_rate); - amp_mod.set_amplitude(0.5); - - let mut generate = |buf: &mut [Sample]| { - for sample in &mut buf.chunks_mut(1) { - let freq_modulation = bipolar_to_unipolar(freq_mod.generate()); - carrier.set_frequency(220.0 + 440.0 * freq_modulation, inv_sample_rate); - let amp_modulation = bipolar_to_unipolar(amp_mod.generate()); - carrier.set_amplitude(amp_modulation); - let signal = carrier.generate(); - let value = (Sample::SCALE as f32 * signal) as Sample; - sample[0] = value; - } - }; - - generate(buffers[0].as_mut()); - generate(buffers[1].as_mut()); - - i2s.start(buffers[0].as_ref()).await.expect("I2S Start"); + output_stream.start(&buffers[0]).await.expect("I2S Start"); let mut index = 1; loop { - if let Err(err) = i2s.send(buffers[index].as_ref()).await { + if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { error!("{}", err); } @@ -77,11 +53,54 @@ async fn main(_spawner: Spawner) { if index >= 3 { index = 0; } - generate(buffers[index].as_mut()); + + waveform.process(&mut buffers[index]); + } +} + +struct Waveform { + inv_sample_rate: f32, + carrier: SineOsc, + freq_mod: SineOsc, + amp_mod: SineOsc, +} + +impl Waveform { + fn new(inv_sample_rate: f32) -> Self { + let carrier = SineOsc::new(); + + let mut freq_mod = SineOsc::new(); + freq_mod.set_frequency(8.0, inv_sample_rate); + freq_mod.set_amplitude(1.0); + + let mut amp_mod = SineOsc::new(); + amp_mod.set_frequency(16.0, inv_sample_rate); + amp_mod.set_amplitude(0.5); + + Self { + inv_sample_rate, + carrier, + freq_mod, + amp_mod, + } + } + + fn process(&mut self, buf: &mut [Sample]) { + for sample in buf.chunks_mut(1) { + let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate()); + self.carrier + .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate); + + let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate()); + self.carrier.set_amplitude(amp_modulation); + + let signal = self.carrier.generate(); + + sample[0] = (Sample::SCALE as f32 * signal) as Sample; + } } } -#[derive(Clone)] struct SineOsc { amplitude: f32, modulo: f32, From 06fb3e425198609131e149c39a2db25aaa8c273c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 21 Nov 2022 11:24:53 +0100 Subject: [PATCH 146/374] docs: add missing README for usb-logger --- embassy-usb-logger/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 embassy-usb-logger/README.md diff --git a/embassy-usb-logger/README.md b/embassy-usb-logger/README.md new file mode 100644 index 00000000..81b0dcd0 --- /dev/null +++ b/embassy-usb-logger/README.md @@ -0,0 +1,15 @@ +# embassy-usb-logger + +USB implementation of the `log` crate. This logger can be used by any device that implements `embassy-usb`. When running, +it will output all logging done through the `log` facade to the USB serial peripheral. + +## Usage + +Add the following embassy task to your application. The `Driver` type is different depending on which HAL you use. + + ```rust +#[embassy_executor::task] +async fn logger_task(driver: Driver<'static, USB>) { + embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); +} +``` From 81dc532d2d041466f2e815f5b9e97a856c47073c Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Sun, 20 Nov 2022 16:08:43 -0500 Subject: [PATCH 147/374] Fix LoRaWAN PHY settings for SX126x driver * Set preamble length to 8 symbols * Set polarity to inverted for received messages --- embassy-lora/src/sx126x/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index ed8cb405..b14d422a 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -87,7 +87,7 @@ where config.rf.spreading_factor.into(), config.rf.bandwidth.into(), config.rf.coding_rate.into(), - 4, + 8, false, true, false, @@ -119,14 +119,14 @@ where config.spreading_factor.into(), config.bandwidth.into(), config.coding_rate.into(), - 4, + 8, 4, false, 0u8, true, false, 0, - false, + true, true, ) .await?; From a6d941fac3d08512f7ef90131d7189ae3aa83bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 00:55:05 +0100 Subject: [PATCH 148/374] Fix txonly/rxonly data pin dir, _from_ram and doc --- embassy-nrf/src/spis.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 3f77c61d..e005f7b9 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -78,7 +78,7 @@ impl<'d, T: Instance> Spis<'d, T> { irq: impl Peripheral

+ 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, - mosi: impl Peripheral

+ 'd, + miso: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, mosi); @@ -87,8 +87,8 @@ impl<'d, T: Instance> Spis<'d, T> { irq, cs.map_into(), sck.map_into(), + Some(miso.map_into()), None, - Some(mosi.map_into()), config, ) } @@ -98,7 +98,7 @@ impl<'d, T: Instance> Spis<'d, T> { irq: impl Peripheral

+ 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, - miso: impl Peripheral

+ 'd, + mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, miso); @@ -107,8 +107,8 @@ impl<'d, T: Instance> Spis<'d, T> { irq, cs.map_into(), sck.map_into(), - Some(miso.map_into()), None, + Some(mosi.map_into()), config, ) } @@ -355,7 +355,7 @@ impl<'d, T: Instance> Spis<'d, T> { } } - /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled. + /// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted. /// Returns number of bytes read. pub fn blocking_read(&mut self, data: &mut [u8]) -> Result { self.blocking_inner(data, &[]).map(|n| n.0) @@ -371,7 +371,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Returns number of bytes transferred `(n_rx, n_tx)`. pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { - self.blocking_inner(read, write) + self.blocking_inner_from_ram(read, write) } /// Simultaneously sends and receives data. @@ -391,7 +391,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Returns number of bytes written. pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result { - self.blocking_inner(&mut [], data).map(|n| n.1) + self.blocking_inner_from_ram(&mut [], data).map(|n| n.1) } /// Reads data from the SPI bus without sending anything. From e6b9722a31fe0a61d1ef66aa796be89cd95b1ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 01:07:59 +0100 Subject: [PATCH 149/374] Remove nrf9160 UARTE fix --- embassy-nrf/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 5726f118..587e19be 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -268,12 +268,5 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); - // Disable UARTE (enabled by default for some reason) - #[cfg(feature = "_nrf9160")] - unsafe { - (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); - (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); - } - peripherals } From 633ffe46aea29bb4c8eec030cbfd6b93867fe79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 01:57:00 +0100 Subject: [PATCH 150/374] config write, docs, add address_match_index --- embassy-nrf/src/lib.rs | 6 ------ embassy-nrf/src/twis.rs | 22 +++++++++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 6c5a3202..7f20f4fd 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -268,11 +268,5 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); - // Disable UARTE (enabled by default for some reason) - #[cfg(feature = "_nrf9160")] - unsafe { - (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); - (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); - } peripherals } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 76952287..b8cb2eeb 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -135,7 +135,7 @@ impl<'d, T: Instance> Twis<'d, T> { // Set address r.address[0].write(|w| unsafe { w.address().bits(config.address0) }); - r.config.modify(|_r, w| w.address0().enabled()); + r.config.write(|w| w.address0().enabled()); if let Some(address1) = config.address1 { r.address[1].write(|w| unsafe { w.address().bits(address1) }); r.config.modify(|_r, w| w.address1().enabled()); @@ -248,6 +248,11 @@ impl<'d, T: Instance> Twis<'d, T> { r.address[r.match_.read().bits() as usize].read().address().bits() } + /// Returns the index of the address matched in the latest command. + pub fn address_match_index(&self) -> usize { + T::regs().match_.read().bits() as _ + } + /// Wait for read, write, stop or error fn blocking_listen_wait(&mut self) -> Result { let r = T::regs(); @@ -588,10 +593,11 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(()) } - /// Listen for commands from an I2C master. - /// + /// Wait for commands from an I2C master. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. + /// To know which one of the addresses were matched, call `address_match` or `address_match_index` pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result { self.setup_listen(buffer, false)?; let status = self.blocking_listen_wait()?; @@ -620,10 +626,11 @@ impl<'d, T: Instance> Twis<'d, T> { // =========================================== - /// Listen for commands from an I2C master with timeout. - /// + /// Wait for commands from an I2C master, with timeout. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. + /// To know which one of the addresses were matched, call `address_match` or `address_match_index` #[cfg(feature = "time")] pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result { self.setup_listen(buffer, false)?; @@ -654,10 +661,11 @@ impl<'d, T: Instance> Twis<'d, T> { // =========================================== - /// Listen asynchronously for commands from an I2C master. - /// + /// Wait asynchronously for commands from an I2C master. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. + /// To know which one of the addresses were matched, call `address_match` or `address_match_index` pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { self.setup_listen(buffer, true)?; let status = self.async_listen_wait().await?; From 908eef2775df870a8c04138cb81400c5b3ecf9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 02:03:34 +0100 Subject: [PATCH 151/374] Change interrupt modify into write --- embassy-rp/src/adc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index cdb752dc..025c6f91 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -65,7 +65,7 @@ impl<'d> Adc<'d> { irq.disable(); irq.set_handler(|_| unsafe { let r = Self::regs(); - r.inte().modify(|w| w.set_fifo(false)); + r.inte().write(|w| w.set_fifo(false)); WAKER.wake(); }); irq.unpend(); @@ -77,7 +77,7 @@ impl<'d> Adc<'d> { async fn wait_for_ready() { let r = Self::regs(); unsafe { - r.inte().modify(|w| w.set_fifo(true)); + r.inte().write(|w| w.set_fifo(true)); compiler_fence(Ordering::SeqCst); poll_fn(|cx| { WAKER.register(cx.waker()); From da9f82f5079ace916b8b5d26fe261cb98fb483fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 02:13:03 +0100 Subject: [PATCH 152/374] Fix pin refs --- embassy-nrf/src/spis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 61c5fe99..44af61a1 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -80,7 +80,7 @@ impl<'d, T: Instance> Spis<'d, T> { miso: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(cs, sck, mosi); + into_ref!(cs, sck, miso); Self::new_inner( spis, irq, @@ -100,7 +100,7 @@ impl<'d, T: Instance> Spis<'d, T> { mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(cs, sck, miso); + into_ref!(cs, sck, mosi); Self::new_inner( spis, irq, From f09745dfe122d723beeba5cd58ae9d6abdf6dddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 02:21:06 +0100 Subject: [PATCH 153/374] embassy-nrf: Default disable UARTE (nrf9160) --- embassy-nrf/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f..b6fe046c 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -267,5 +267,12 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); + // Disable UARTE (enabled by default for some reason) + #[cfg(feature = "_nrf9160")] + unsafe { + (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); + (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); + } + peripherals } From f47481787279ce0809cf983deb272815c3654b85 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 22 Nov 2022 09:35:42 +0100 Subject: [PATCH 154/374] doc: add README to embassy-macro Documents the main and task macros. --- embassy-macros/README.md | 21 +++++++++++++++++ embassy-macros/src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 embassy-macros/README.md diff --git a/embassy-macros/README.md b/embassy-macros/README.md new file mode 100644 index 00000000..d1d6f4cc --- /dev/null +++ b/embassy-macros/README.md @@ -0,0 +1,21 @@ +# embassy-macros + +An [Embassy](https://embassy.dev) project. + +Macros for creating the main entry point and tasks that can be spawned by `embassy-executor`. + +NOTE: The macros are re-exported by the `embassy-executor` crate which should be used instead of adding a direct dependency on the `embassy-macros` crate. + +## Minimum supported Rust version (MSRV) + +The `task` and `main` macros require the type alias impl trait (TAIT) nightly feature in order to compile. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index ec8498f9..f5df2a26 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -1,3 +1,4 @@ +#![doc = include_str!("../README.md")] extern crate proc_macro; use proc_macro::TokenStream; @@ -6,6 +7,36 @@ mod macros; mod util; use macros::*; +/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how +/// many concurrent tasks can be spawned (default is 1) for the function. +/// +/// +/// The following restrictions apply: +/// +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * The optional `pool_size` attribute must be 1 or greater. +/// +/// +/// ## Examples +/// +/// Declaring a task taking no arguments: +/// +/// ``` rust +/// #[embassy_executor::task] +/// async fn mytask() { +/// // Function body +/// } +/// ``` +/// +/// Declaring a task with a given pool size: +/// +/// ``` rust +/// #[embassy_executor::task(pool_size = 4)] +/// async fn mytask() { +/// // Function body +/// } +/// ``` #[proc_macro_attribute] pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as syn::AttributeArgs); @@ -14,6 +45,24 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { task::run(args, f).unwrap_or_else(|x| x).into() } +/// Creates a new `executor` instance and declares an application entry point spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` #[proc_macro_attribute] pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as syn::AttributeArgs); From 51233c0357c5ef694c6751c2cf62c54777e2baf0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 22 Nov 2022 14:48:42 +0100 Subject: [PATCH 155/374] doc: update cargo manifests with keywords --- embassy-executor/Cargo.toml | 8 +++++++- embassy-macros/Cargo.toml | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d0f51646..910d6fa3 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -3,7 +3,13 @@ name = "embassy-executor" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" - +description = "async/await executor designed for embedded usage" +repository = "https://github.com/embassy-rs/embassy" +categories = [ + "embedded", + "no-std", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 91d5ec8a..98e4d1c7 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -3,6 +3,13 @@ name = "embassy-macros" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" +description = "macros for creating the entry point and tasks for embassy-executor" +repository = "https://github.com/embassy-rs/embassy" +categories = [ + "embedded", + "no-std", + "asynchronous", +] [dependencies] syn = { version = "1.0.76", features = ["full", "extra-traits"] } From 536b6a2de5c5342a27dc1095f5642792fb6d860b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 22 Nov 2022 21:55:10 +0800 Subject: [PATCH 156/374] sync/signal: Implement Default for Signal --- embassy-sync/src/signal.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index c3c10a8a..bea67d8b 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -56,6 +56,15 @@ where } } +impl Default for Signal +where + M: RawMutex, +{ + fn default() -> Self { + Self::new() + } +} + impl Signal where M: RawMutex, From a074cd0625d68e72694c6575063ae53a840d12dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ardelean=20C=C4=83lin=20Petru?= Date: Tue, 22 Nov 2022 16:56:04 +0200 Subject: [PATCH 157/374] Update gpiote.rs Adding these changes enables us to define a channel using a mutable reference to `GPIOTE_CH(n)`, similar to how we can do with other drivers. So instead of using: ```rust let freq_in = InputChannel::new( p.GPIOTE_CH0, Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up), embassy_nrf::gpiote::InputChannelPolarity::HiToLo, ); ``` we can use: ```rust let freq_in = InputChannel::new( &mut p.GPIOTE_CH0, Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up), embassy_nrf::gpiote::InputChannelPolarity::HiToLo, ); ``` --- embassy-nrf/src/gpiote.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 25ad9049..4f11f33e 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -419,6 +419,12 @@ macro_rules! impl_channel { $number as usize } } + impl sealed::Channel for &mut peripherals::$type {} + impl Channel for &mut peripherals::$type { + fn number(&self) -> usize { + $number as usize + } + } }; } From 64c2e1b9b670fda7446a0df6eeb2db5dce2aa1c2 Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Tue, 22 Nov 2022 17:35:38 +0200 Subject: [PATCH 158/374] Switched to PeripheralRef for channel. --- embassy-nrf/src/gpiote.rs | 43 ++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 4f11f33e..6fac2c37 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -2,7 +2,7 @@ use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; +use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef, into_ref}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; @@ -148,21 +148,23 @@ impl Iterator for BitIter { /// GPIOTE channel driver in input mode pub struct InputChannel<'d, C: Channel, T: GpioPin> { - ch: C, + _ch: PeripheralRef<'d, C>, pin: Input<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self.ch.number(); + let num = self._ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } } impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { - pub fn new(ch: C, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { + pub fn new(ch: impl Peripheral

+ 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { + into_ref!(ch); + let g = regs(); let num = ch.number(); @@ -183,12 +185,12 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { g.events_in[num].reset(); - InputChannel { ch, pin } + InputChannel { _ch: ch, pin } } pub async fn wait(&self) { let g = regs(); - let num = self.ch.number(); + let num = self._ch.number(); // Enable interrupt g.events_in[num].reset(); @@ -209,27 +211,28 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event { let g = regs(); - Event::from_reg(&g.events_in[self.ch.number()]) + Event::from_reg(&g.events_in[self._ch.number()]) } } /// GPIOTE channel driver in output mode pub struct OutputChannel<'d, C: Channel, T: GpioPin> { - ch: C, + _ch: PeripheralRef<'d, C>, _pin: Output<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self.ch.number(); + let num = self._ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } } impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { - pub fn new(ch: C, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { + pub fn new(ch: impl Peripheral

+ 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { + into_ref!(ch); let g = regs(); let num = ch.number(); @@ -252,47 +255,47 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { unsafe { w.psel().bits(pin.pin.pin.pin()) } }); - OutputChannel { ch, _pin: pin } + OutputChannel { _ch: ch, _pin: pin } } /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); - g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_out[self._ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task set` (set associated pin high). #[cfg(not(feature = "nrf51"))] pub fn set(&self) { let g = regs(); - g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_set[self._ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task clear` (set associated pin low). #[cfg(not(feature = "nrf51"))] pub fn clear(&self) { let g = regs(); - g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_clr[self._ch.number()].write(|w| unsafe { w.bits(1) }); } /// Returns the OUT task, for use with PPI. pub fn task_out(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_out[self.ch.number()]) + Task::from_reg(&g.tasks_out[self._ch.number()]) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_clr(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_clr[self.ch.number()]) + Task::from_reg(&g.tasks_clr[self._ch.number()]) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_set(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_set[self.ch.number()]) + Task::from_reg(&g.tasks_set[self._ch.number()]) } } @@ -419,12 +422,6 @@ macro_rules! impl_channel { $number as usize } } - impl sealed::Channel for &mut peripherals::$type {} - impl Channel for &mut peripherals::$type { - fn number(&self) -> usize { - $number as usize - } - } }; } From e7c876d7444fb24ad854bd7339c480874f72dbe2 Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Tue, 22 Nov 2022 17:36:22 +0200 Subject: [PATCH 159/374] Changed pin to private as it is for OutputChannel --- embassy-nrf/src/gpiote.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 6fac2c37..e3be0240 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -149,7 +149,7 @@ impl Iterator for BitIter { /// GPIOTE channel driver in input mode pub struct InputChannel<'d, C: Channel, T: GpioPin> { _ch: PeripheralRef<'d, C>, - pin: Input<'d, T>, + _pin: Input<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { @@ -185,7 +185,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { g.events_in[num].reset(); - InputChannel { _ch: ch, pin } + InputChannel { _ch: ch, _pin: pin } } pub async fn wait(&self) { @@ -443,11 +443,11 @@ mod eh02 { type Error = Infallible; fn is_high(&self) -> Result { - Ok(self.pin.is_high()) + Ok(self._pin.is_high()) } fn is_low(&self) -> Result { - Ok(self.pin.is_low()) + Ok(self._pin.is_low()) } } } @@ -462,11 +462,11 @@ mod eh1 { impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> { fn is_high(&self) -> Result { - Ok(self.pin.is_high()) + Ok(self._pin.is_high()) } fn is_low(&self) -> Result { - Ok(self.pin.is_low()) + Ok(self._pin.is_low()) } } } From 4f2f3757773fb30700c3c6ee7ab98cd6e38406a3 Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Tue, 22 Nov 2022 17:45:05 +0200 Subject: [PATCH 160/374] Corrected order of use statements. --- embassy-nrf/src/gpiote.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index e3be0240..4d5fa62b 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -2,7 +2,7 @@ use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef, into_ref}; +use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; From cf900a8a3f048428cc1209763f4188366818ab8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 22:10:04 +0100 Subject: [PATCH 161/374] Rename write to respond_to_read --- embassy-nrf/src/twis.rs | 52 ++++++++++++++++++++---------------- examples/nrf/src/bin/twis.rs | 11 ++++---- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index b8cb2eeb..4091b017 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -507,7 +507,7 @@ impl<'d, T: Instance> Twis<'d, T> { }) } - fn setup_write_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { + fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { let r = T::regs(); compiler_fence(SeqCst); @@ -532,14 +532,14 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(()) } - fn setup_write(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { - match self.setup_write_from_ram(wr_buffer, inten) { + fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { + match self.setup_respond_from_ram(wr_buffer, inten) { Ok(_) => Ok(()), Err(Error::DMABufferNotInDataMemory) => { trace!("Copying TWIS tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; tx_ram_buf.copy_from_slice(wr_buffer); - self.setup_write_from_ram(&tx_ram_buf, inten) + self.setup_respond_from_ram(&tx_ram_buf, inten) } Err(error) => Err(error), } @@ -609,18 +609,19 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - /// Write to an I2C master. + /// Respond to an I2C master READ command. /// Returns the number of bytes written. /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { - self.setup_write(buffer, false)?; + pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result { + self.setup_respond(buffer, false)?; self.blocking_wait() } - /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result { - self.setup_write_from_ram(buffer, false)?; + /// Same as [`blocking_respond_to_read`](Twis::blocking_respond_to_read) but will fail instead of copying data into RAM. + /// Consult the module level documentation to learn more. + pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result { + self.setup_respond_from_ram(buffer, false)?; self.blocking_wait() } @@ -643,19 +644,24 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - /// Write to an I2C master with timeout. + /// Respond to an I2C master READ command with timeout. /// Returns the number of bytes written. - /// See [`blocking_write`]. + /// See [`blocking_respond_to_read`]. #[cfg(feature = "time")] - pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { - self.setup_write(buffer, false)?; + pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { + self.setup_respond(buffer, false)?; self.blocking_wait_timeout(timeout) } - /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Same as [`blocking_respond_to_read_timeout`](Twis::blocking_respond_to_read_timeout) but will fail instead of copying data into RAM. + /// Consult the module level documentation to learn more. #[cfg(feature = "time")] - pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { - self.setup_write_from_ram(buffer, false)?; + pub fn blocking_respond_to_read_from_ram_timeout( + &mut self, + buffer: &[u8], + timeout: Duration, + ) -> Result { + self.setup_respond_from_ram(buffer, false)?; self.blocking_wait_timeout(timeout) } @@ -677,18 +683,18 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - /// Async write to an I2C master. + /// Respond to an I2C master READ command, asynchronously. /// Returns the number of bytes written. /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. - pub async fn write(&mut self, buffer: &[u8]) -> Result { - self.setup_write(buffer, true)?; + pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { + self.setup_respond(buffer, true)?; self.async_wait().await } - /// Same as [`write`](Twis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result { - self.setup_write_from_ram(buffer, true)?; + /// Same as [`respond_to_read`](Twis::respond_to_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result { + self.setup_respond_from_ram(buffer, true)?; self.async_wait().await } } diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs index a34bb271..54cba949 100644 --- a/examples/nrf/src/bin/twis.rs +++ b/examples/nrf/src/bin/twis.rs @@ -22,20 +22,21 @@ async fn main(_spawner: Spawner) { info!("Listening..."); loop { + let response = [1, 2, 3, 4, 5, 6, 7, 8]; + // This buffer is used if the i2c master performs a Write or WriteRead let mut buf = [0u8; 16]; - let tx_buf = [1, 2, 3, 4, 5, 6, 7, 8]; match i2c.listen(&mut buf).await { Ok(Command::Read) => { - info!("Got READ command. Writing back data:\n{:?}\n", tx_buf); - if let Err(e) = i2c.write(&tx_buf).await { + info!("Got READ command. Respond with data:\n{:?}\n", response); + if let Err(e) = i2c.respond_to_read(&response).await { error!("{:?}", e); } } Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]), Ok(Command::WriteRead(n)) => { info!("Got WRITE/READ command with data:\n{:?}", buf[..n]); - info!("Writing back data:\n{:?}\n", tx_buf); - if let Err(e) = i2c.write(&tx_buf).await { + info!("Respond with data:\n{:?}\n", response); + if let Err(e) = i2c.respond_to_read(&response).await { error!("{:?}", e); } } From 28991d77941da2d77fcb242e025af06ceb936460 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 23 Nov 2022 17:30:58 +0530 Subject: [PATCH 162/374] added blinky example for stm32f0 --- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f0/src/bin/blinky.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f0/src/bin/blinky.rs diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a56c546e..d4dc81cb 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ panic-probe = "0.3" 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", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f030f4", "time-driver-any"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any"] } diff --git a/examples/stm32f0/src/bin/blinky.rs b/examples/stm32f0/src/bin/blinky.rs new file mode 100644 index 00000000..9f923399 --- /dev/null +++ b/examples/stm32f0/src/bin/blinky.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +// main is itself an async function. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + //PA5 is the onboard LED on the Nucleo F091RC + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} From eae67d0be888d12e71bc3340279bab85666c05ae Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Wed, 23 Nov 2022 14:16:18 +0200 Subject: [PATCH 163/374] Review comments. Corrected unused fields. --- embassy-nrf/src/gpiote.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 4d5fa62b..7f7468a2 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -148,14 +148,14 @@ impl Iterator for BitIter { /// GPIOTE channel driver in input mode pub struct InputChannel<'d, C: Channel, T: GpioPin> { - _ch: PeripheralRef<'d, C>, - _pin: Input<'d, T>, + ch: PeripheralRef<'d, C>, + pin: Input<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self._ch.number(); + let num = self.ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } @@ -185,12 +185,12 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { g.events_in[num].reset(); - InputChannel { _ch: ch, _pin: pin } + InputChannel { ch, pin } } pub async fn wait(&self) { let g = regs(); - let num = self._ch.number(); + let num = self.ch.number(); // Enable interrupt g.events_in[num].reset(); @@ -211,20 +211,20 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event { let g = regs(); - Event::from_reg(&g.events_in[self._ch.number()]) + Event::from_reg(&g.events_in[self.ch.number()]) } } /// GPIOTE channel driver in output mode pub struct OutputChannel<'d, C: Channel, T: GpioPin> { - _ch: PeripheralRef<'d, C>, + ch: PeripheralRef<'d, C>, _pin: Output<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self._ch.number(); + let num = self.ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } @@ -255,47 +255,47 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { unsafe { w.psel().bits(pin.pin.pin.pin()) } }); - OutputChannel { _ch: ch, _pin: pin } + OutputChannel { ch, _pin: pin } } /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); - g.tasks_out[self._ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task set` (set associated pin high). #[cfg(not(feature = "nrf51"))] pub fn set(&self) { let g = regs(); - g.tasks_set[self._ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task clear` (set associated pin low). #[cfg(not(feature = "nrf51"))] pub fn clear(&self) { let g = regs(); - g.tasks_clr[self._ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); } /// Returns the OUT task, for use with PPI. pub fn task_out(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_out[self._ch.number()]) + Task::from_reg(&g.tasks_out[self.ch.number()]) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_clr(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_clr[self._ch.number()]) + Task::from_reg(&g.tasks_clr[self.ch.number()]) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_set(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_set[self._ch.number()]) + Task::from_reg(&g.tasks_set[self.ch.number()]) } } @@ -443,11 +443,11 @@ mod eh02 { type Error = Infallible; fn is_high(&self) -> Result { - Ok(self._pin.is_high()) + Ok(self.pin.is_high()) } fn is_low(&self) -> Result { - Ok(self._pin.is_low()) + Ok(self.pin.is_low()) } } } @@ -462,11 +462,11 @@ mod eh1 { impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> { fn is_high(&self) -> Result { - Ok(self._pin.is_high()) + Ok(self.pin.is_high()) } fn is_low(&self) -> Result { - Ok(self._pin.is_low()) + Ok(self.pin.is_low()) } } } From 50c5cc5db64f7ddf8566626f92c0694ac9ad984e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 23 Nov 2022 13:17:05 +0100 Subject: [PATCH 164/374] fix: revert race condition introduced for riscv --- embassy-executor/src/arch/riscv32.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 76eb8b11..2a4b006d 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -55,11 +55,19 @@ impl Executor { unsafe { self.inner.poll(); // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - if !SIGNAL_WORK_THREAD_MODE.fetch_and(false, Ordering::SeqCst) { - core::arch::asm!("wfi"); - } + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here } } } From 5aad2129ef2eefd6ea2f2bda05f6c0e6172ba1c8 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 23 Nov 2022 17:51:43 +0530 Subject: [PATCH 165/374] added the runner for stm32f091rc --- examples/stm32f0/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml index d1b1cd0b..16abc29b 100644 --- a/examples/stm32f0/.cargo/config.toml +++ b/examples/stm32f0/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv6m-none-eabi] -runner = 'probe-run --chip STM32F030F4Px' +runner = 'probe-run --chip STM32F091RCTX' [build] target = "thumbv6m-none-eabi" From 04a7d976733e021395ff26e26dfa983e67b773a0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 22 Nov 2022 22:04:42 +0100 Subject: [PATCH 166/374] refactor: autodetect macro variant Export all main macro per target architecture from embassy-macros, and select the appropriate macro in embassy-executor. --- embassy-executor/Cargo.toml | 5 +- embassy-executor/src/lib.rs | 10 ++- embassy-macros/Cargo.toml | 4 -- embassy-macros/src/lib.rs | 81 +++++++++++++++++++++- embassy-macros/src/macros/main.rs | 108 ++++++++++++++++-------------- 5 files changed, 145 insertions(+), 63 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 45b0955b..6fa1dd7f 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -29,9 +29,8 @@ flavors = [ [features] default = [] -std = ["embassy-macros/std", "critical-section/std"] -wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"] -riscv = ["embassy-macros/riscv"] +std = ["critical-section/std"] +wasm = ["dep:wasm-bindgen", "dep:js-sys"] # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index e4cbd04b..4c7e2f4c 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -8,18 +8,22 @@ pub(crate) mod fmt; #[cfg(feature = "nightly")] -pub use embassy_macros::{main, task}; +pub use embassy_macros::task; cfg_if::cfg_if! { if #[cfg(cortex_m)] { #[path="arch/cortex_m.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_cortex_m as main; } else if #[cfg(target_arch="riscv32")] { #[path="arch/riscv32.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_riscv as main; } else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] { #[path="arch/xtensa.rs"] @@ -30,11 +34,15 @@ cfg_if::cfg_if! { #[path="arch/wasm.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_wasm as main; } else if #[cfg(feature="std")] { #[path="arch/std.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_std as main; } } diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 9b83771c..5c612c99 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -21,9 +21,5 @@ proc-macro2 = "1.0.29" proc-macro = true [features] -std = [] -wasm = [] -riscv = [] - # Enabling this cause interrupt::take! to require embassy-executor rtos-trace-interrupt = [] diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index f5df2a26..d2c696c7 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -45,7 +45,7 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { task::run(args, f).unwrap_or_else(|x| x).into() } -/// Creates a new `executor` instance and declares an application entry point spawning the corresponding function body as an async task. +/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task. /// /// The following restrictions apply: /// @@ -64,10 +64,85 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { /// } /// ``` #[proc_macro_attribute] -pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { +pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as syn::AttributeArgs); let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(args, f).unwrap_or_else(|x| x).into() + main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into() +} + +/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(args, f, main::riscv()).unwrap_or_else(|x| x).into() +} + +/// Creates a new `executor` instance and declares an application entry point for STD spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(args, f, main::std()).unwrap_or_else(|x| x).into() +} + +/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into() } #[proc_macro_attribute] diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index 54806847..18f7c36c 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -7,7 +7,62 @@ use crate::util::ctxt::Ctxt; #[derive(Debug, FromMeta)] struct Args {} -pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result { +pub fn riscv() -> TokenStream { + quote! { + #[riscv_rt::entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + +pub fn cortex_m() -> TokenStream { + quote! { + #[cortex_m_rt::entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + +pub fn wasm() -> TokenStream { + quote! { + #[wasm_bindgen::prelude::wasm_bindgen(start)] + pub fn main() -> Result<(), wasm_bindgen::JsValue> { + static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new(); + let executor = EXECUTOR.init(::embassy_executor::Executor::new()); + + executor.start(|spawner| { + spawner.spawn(__embassy_main(spawner)).unwrap(); + }); + + Ok(()) + } + } +} + +pub fn std() -> TokenStream { + quote! { + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + +pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result { #[allow(unused_variables)] let args = Args::from_list(&args).map_err(|e| e.write_errors())?; @@ -30,57 +85,6 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result Result<(), wasm_bindgen::JsValue> { - static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new(); - let executor = EXECUTOR.init(::embassy_executor::Executor::new()); - - executor.start(|spawner| { - spawner.spawn(__embassy_main(spawner)).unwrap(); - }); - - Ok(()) - } - }; - - #[cfg(all(feature = "std", not(feature = "wasm"), not(feature = "riscv")))] - let main = quote! { - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - }; - - #[cfg(all(not(feature = "std"), not(feature = "wasm"), not(feature = "riscv")))] - let main = quote! { - #[cortex_m_rt::entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - }; - - #[cfg(all(not(feature = "std"), not(feature = "wasm"), feature = "riscv"))] - let main = quote! { - #[riscv_rt::entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - }; - let result = quote! { #[::embassy_executor::task()] async fn __embassy_main(#fargs) { From db7e153fc012aff0cc2c6529d5d26a3d184051a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Nov 2022 14:49:40 +0100 Subject: [PATCH 167/374] executor: enable features for docs.rs Otherwise the non-raw executor and the macros don't show up. --- embassy-executor/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 6fa1dd7f..5acad95e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -27,6 +27,9 @@ flavors = [ { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, ] +[package.metadata.docs.rs] +features = ["std", "nightly", "defmt"] + [features] default = [] std = ["critical-section/std"] From 758f5d7ea29f1df14d5ef59c82e4b7f22545d775 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Nov 2022 14:53:18 +0100 Subject: [PATCH 168/374] Release embassy-executor v0.1.1 --- embassy-executor/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 5acad95e..c2868eb9 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" From 89821846d77d85d940b87cfa4f62171bd532b27c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 23 Nov 2022 14:48:51 +0100 Subject: [PATCH 169/374] fix: add required metadata for embassy-boot --- embassy-boot/boot/Cargo.toml | 12 +++++++++++- embassy-boot/{ => boot}/README.md | 0 embassy-boot/boot/src/lib.rs | 2 +- embassy-boot/nrf/README.md | 26 ++++++++++++++++++++++++++ embassy-boot/nrf/src/lib.rs | 2 +- embassy-boot/stm32/README.md | 27 ++++++++++++++++++++------- embassy-boot/stm32/src/lib.rs | 2 +- 7 files changed, 60 insertions(+), 11 deletions(-) rename embassy-boot/{ => boot}/README.md (100%) create mode 100644 embassy-boot/nrf/README.md diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 54c67a37..0cc6a058 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -2,13 +2,23 @@ edition = "2021" name = "embassy-boot" version = "0.1.0" -description = "Bootloader using Embassy" +description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." license = "MIT OR Apache-2.0" +repository = "https://github.com/embassy-rs/embassy" +categories = [ + "embedded", + "no-std", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/boot/src/" target = "thumbv7em-none-eabi" +features = ["defmt"] + +[package.metadata.docs.rs] +features = ["defmt"] [lib] diff --git a/embassy-boot/README.md b/embassy-boot/boot/README.md similarity index 100% rename from embassy-boot/README.md rename to embassy-boot/boot/README.md diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 429323ec..76b14bc8 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,7 +1,7 @@ #![feature(type_alias_impl_trait)] #![no_std] #![warn(missing_docs)] -#![doc = include_str!("../../README.md")] +#![doc = include_str!("../README.md")] mod fmt; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; diff --git a/embassy-boot/nrf/README.md b/embassy-boot/nrf/README.md new file mode 100644 index 00000000..02f35c0a --- /dev/null +++ b/embassy-boot/nrf/README.md @@ -0,0 +1,26 @@ +# embassy-boot-nrf + +An [Embassy](https://embassy.dev) project. + +An adaptation of `embassy-boot` for nRF. + +## Features + +* Load applications with our without the softdevice. +* Configure bootloader partitions based on linker script. +* Using watchdog timer to detect application failure. + + +## Minimum supported Rust version (MSRV) + +`embassy-boot-nrf` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 82475d1e..205bbd6d 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] -#![doc = include_str!("../../README.md")] +#![doc = include_str!("../README.md")] mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; diff --git a/embassy-boot/stm32/README.md b/embassy-boot/stm32/README.md index a82b730b..cb134b53 100644 --- a/embassy-boot/stm32/README.md +++ b/embassy-boot/stm32/README.md @@ -1,11 +1,24 @@ -# Bootloader for STM32 +# embassy-boot-stm32 -The bootloader uses `embassy-boot` to interact with the flash. +An [Embassy](https://embassy.dev) project. -# Usage +An adaptation of `embassy-boot` for STM32. -Flash the bootloader +## Features -``` -cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx -``` +* Configure bootloader partitions based on linker script. +* Load applications from active partition. + +## Minimum supported Rust version (MSRV) + +`embassy-boot-stm32` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index d549eccc..82f712c4 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] -#![doc = include_str!("../../README.md")] +#![doc = include_str!("../README.md")] mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; From 09077f133d4962ef4fbe6ae7acc1420fb0da768c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 25 Nov 2022 15:51:31 +0100 Subject: [PATCH 170/374] fix: bump embassy-boot version --- embassy-boot/boot/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 0cc6a058..ae4efbd2 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot" -version = "0.1.0" +version = "0.1.1" description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" From 1e2fb0459d8546ba658bb9fe150be5f1f537b48e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Nov 2022 23:31:31 +0100 Subject: [PATCH 171/374] Switch to async-fn-in-trait --- embassy-embedded-hal/Cargo.toml | 2 +- embassy-embedded-hal/src/adapter.rs | 79 +-- embassy-embedded-hal/src/lib.rs | 6 +- .../src/shared_bus/asynch/i2c.rs | 107 ++- .../src/shared_bus/asynch/spi.rs | 70 +- embassy-lora/Cargo.toml | 2 +- embassy-net/Cargo.toml | 4 +- embassy-net/src/lib.rs | 6 +- embassy-net/src/tcp.rs | 118 ++-- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/buffered_uarte.rs | 66 +- embassy-nrf/src/gpiote.rs | 62 +- embassy-nrf/src/lib.rs | 6 +- embassy-nrf/src/spim.rs | 31 +- embassy-nrf/src/twim.rs | 28 +- embassy-nrf/src/uarte.rs | 26 +- embassy-nrf/src/usb.rs | 393 +++++------ embassy-rp/Cargo.toml | 4 +- embassy-rp/src/gpio.rs | 63 +- embassy-rp/src/i2c.rs | 75 +-- embassy-rp/src/lib.rs | 3 +- embassy-rp/src/spi.rs | 32 +- embassy-rp/src/uart/buffered.rs | 60 +- embassy-rp/src/usb.rs | 583 ++++++++--------- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/exti.rs | 36 +- embassy-stm32/src/i2c/v2.rs | 34 +- embassy-stm32/src/lib.rs | 6 +- embassy-stm32/src/spi/mod.rs | 32 +- embassy-stm32/src/usart/buffered.rs | 66 +- embassy-stm32/src/usb/usb.rs | 612 ++++++++---------- embassy-sync/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- embassy-usb-driver/src/lib.rs | 69 +- examples/nrf/Cargo.toml | 4 +- examples/rp/Cargo.toml | 4 +- examples/std/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 6 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 5 - rust-toolchain.toml | 2 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 2 +- 47 files changed, 1153 insertions(+), 1579 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 85ee856a..fa74be8c 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -20,7 +20,7 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true } +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index 1c43f015..3680984f 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -38,32 +38,31 @@ where E: embedded_hal_1::i2c::Error + 'static, T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.wrapped.read(address, buffer) } + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, buffer) } - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.wrapped.write(address, bytes) } + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, bytes) } - fn write_read<'a>(&'a mut self, address: u8, bytes: &'a [u8], buffer: &'a mut [u8]) -> Self::WriteReadFuture<'a> { - async move { self.wrapped.write_read(address, bytes, buffer) } + async fn write_read<'a>( + &'a mut self, + address: u8, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Result<(), Self::Error> { + self.wrapped.write_read(address, bytes, buffer) } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), Self::Error> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } @@ -84,23 +83,17 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Self::TransferFuture<'a> { - async move { - // Ensure we write the expected bytes - for i in 0..core::cmp::min(read.len(), write.len()) { - read[i] = write[i].clone(); - } - self.wrapped.transfer(read)?; - Ok(()) + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + // Ensure we write the expected bytes + for i in 0..core::cmp::min(read.len(), write.len()) { + read[i] = write[i].clone(); } + self.wrapped.transfer(read)?; + Ok(()) } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { - async move { todo!() } + async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> { + todo!() } } @@ -109,10 +102,8 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } @@ -121,13 +112,9 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - self.wrapped.write(data)?; - Ok(()) - } + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data)?; + Ok(()) } } @@ -136,13 +123,9 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - self.wrapped.transfer(data)?; - Ok(()) - } + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(data)?; + Ok(()) } } @@ -192,7 +175,7 @@ where } type FlushFuture<'a> = impl Future> + 'a where T: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + fn flush(&mut self) -> Result<(), Self::Error> { async move { self.wrapped.bflush() } } } diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index a12a3a3a..8da04222 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,5 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index 0bc6afd9..c5e1fd41 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -22,7 +22,6 @@ //! let i2c_dev2 = I2cDevice::new(i2c_bus); //! let mpu = Mpu6050::new(i2c_dev2); //! ``` -use core::future::Future; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; @@ -55,53 +54,39 @@ where M: RawMutex + 'static, BUS: i2c::I2c + 'static, { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, wr_buffer: &'a [u8], rd_buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.write_read(address, wr_buffer, rd_buffer) - .await - .map_err(I2cDeviceError::I2c)?; - Ok(()) - } + ) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.write_read(address, wr_buffer, rd_buffer) + .await + .map_err(I2cDeviceError::I2c)?; + Ok(()) } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), I2cDeviceError> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } @@ -136,55 +121,41 @@ where M: RawMutex + 'static, BUS: i2c::I2c + SetConfig + 'static, { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, wr_buffer: &'a [u8], rd_buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - bus.write_read(address, wr_buffer, rd_buffer) - .await - .map_err(I2cDeviceError::I2c)?; - Ok(()) - } + ) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.write_read(address, wr_buffer, rd_buffer) + .await + .map_err(I2cDeviceError::I2c)?; + Ok(()) } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), I2cDeviceError> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index a3814d6d..d2571665 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -65,33 +65,25 @@ where { type Bus = BUS; - type TransactionFuture<'a, R, F, Fut> = impl Future> + 'a + async fn transaction(&mut self, f: F) -> Result where - Self: 'a, R: 'a, F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a; - - fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut> - where - R: 'a, - F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a, + F: FnOnce(*mut Self::Bus) -> Fut, + Fut: Future::Error>>, { - async move { - let mut bus = self.bus.lock().await; - self.cs.set_low().map_err(SpiDeviceError::Cs)?; + let mut bus = self.bus.lock().await; + self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let f_res = f(&mut *bus).await; - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush().await; - let cs_res = self.cs.set_high(); + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; + let f_res = f_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) - } + Ok(f_res) } } @@ -130,33 +122,25 @@ where { type Bus = BUS; - type TransactionFuture<'a, R, F, Fut> = impl Future> + 'a + async fn transaction(&mut self, f: F) -> Result where - Self: 'a, R: 'a, F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a; - - fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut> - where - R: 'a, - F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a, + F: FnOnce(*mut Self::Bus) -> Fut, + Fut: Future::Error>>, { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let f_res = f(&mut *bus).await; - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush().await; - let cs_res = self.cs.set_high(); + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; + let f_res = f_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) - } + Ok(f_res) } } diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index dc200417..cbe78e59 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -32,7 +32,7 @@ embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 76217075..0ac53b50 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -42,7 +42,7 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embedded-io = { version = "0.3.1", optional = true } +embedded-io = { version = "0.4.0", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } @@ -52,7 +52,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" atomic-polyfill = "1.0.1" -embedded-nal-async = { version = "0.2.0", optional = true } +embedded-nal-async = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0", optional = true } [dependencies.smoltcp] version = "0.8.0" diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 4d30550d..edb96984 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index f3bd2361..85d9e5ee 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -271,8 +271,6 @@ impl<'d> TcpIo<'d> { #[cfg(feature = "nightly")] mod embedded_io_impls { - use core::future::Future; - use super::*; impl embedded_io::Error for ConnectError { @@ -292,30 +290,18 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.io.read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.io.read(buf).await } } impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.io.write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.io.write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.io.flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.io.flush().await } } @@ -324,12 +310,8 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpReader<'d> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.io.read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.io.read(buf).await } } @@ -338,27 +320,18 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.io.write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.io.write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.io.flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.io.flush().await } } } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { - use core::future::Future; use core::mem::MaybeUninit; use core::ptr::NonNull; @@ -385,28 +358,29 @@ pub mod client { { type Error = Error; type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm; - type ConnectFuture<'m> = impl Future, Self::Error>> + 'm - where - Self: 'm; - fn connect<'m>(&'m self, remote: embedded_nal_async::SocketAddr) -> Self::ConnectFuture<'m> { - async move { - let addr: crate::IpAddress = match remote.ip() { - IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), - #[cfg(feature = "proto-ipv6")] - IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), - #[cfg(not(feature = "proto-ipv6"))] - IpAddr::V6(_) => panic!("ipv6 support not enabled"), - }; - let remote_endpoint = (addr, remote.port()); - let mut socket = TcpConnection::new(&self.stack, self.state)?; - socket - .socket - .connect(remote_endpoint) - .await - .map_err(|_| Error::ConnectionReset)?; - Ok(socket) - } + async fn connect<'a>( + &'a self, + remote: embedded_nal_async::SocketAddr, + ) -> Result, Self::Error> + where + Self: 'a, + { + let addr: crate::IpAddress = match remote.ip() { + IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), + #[cfg(feature = "proto-ipv6")] + IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), + #[cfg(not(feature = "proto-ipv6"))] + IpAddr::V6(_) => panic!("ipv6 support not enabled"), + }; + let remote_endpoint = (addr, remote.port()); + let mut socket = TcpConnection::new(&self.stack, self.state)?; + socket + .socket + .connect(remote_endpoint) + .await + .map_err(|_| Error::ConnectionReset)?; + Ok(socket) } } @@ -445,32 +419,20 @@ pub mod client { impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.socket.read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.socket.read(buf).await } } impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.socket.write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.socket.write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.socket.flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.socket.flush().await } } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 67b6bec4..6b06d5d0 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -75,8 +75,8 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} -embedded-io = { version = "0.3.1", features = ["async"], optional = true } +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} +embedded-io = { version = "0.4.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9c8fe65f..ea25236f 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::{poll_fn, Future}; +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -341,32 +341,20 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUar } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await } } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.inner_read(buf).await } } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -375,12 +363,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -389,38 +373,22 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRea } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner_flush().await } } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_flush().await } } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 7f7468a2..7467dbc6 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -473,71 +473,49 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use futures::FutureExt; - use super::*; impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_high().await) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_low().await) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_rising_edge().await) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_falling_edge().await) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_any_edge().await) } } impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_high().await) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_low().await) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_rising_edge().await) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_falling_edge().await) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_any_edge().await) } } } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index dc6f1686..9054bc30 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -43,7 +43,11 @@ //! mutable slices always reside in RAM. #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index d821d235..7bb4e39f 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -477,45 +477,34 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; use super::*; impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } + async fn flush(&mut self) -> Result<(), Error> { + Ok(()) } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spim<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(words) + async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> { + self.read(words).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spim<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(data) + async fn write(&mut self, data: &[u8]) -> Result<(), Error> { + self.write(data).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> { - self.transfer(rx, tx) + async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { + self.transfer(rx, tx).await } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { - self.transfer_in_place(words) + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { + self.transfer_in_place(words).await } } } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 8d6171fa..4eafd18c 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -841,39 +841,31 @@ mod eh1 { mod eha { use super::*; impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(address, buffer) + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Error> { + self.read(address, buffer).await } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(address, bytes) + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Error> { + self.write(address, bytes).await } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, wr_buffer: &'a [u8], rd_buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - self.write_read(address, wr_buffer, rd_buffer) + ) -> Result<(), Error> { + self.write_read(address, wr_buffer, rd_buffer).await } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), Error> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 636d6c7a..63df1b68 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -986,7 +986,7 @@ mod eha { type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + fn flush(&mut self) -> Result<(), Self::Error> { async move { Ok(()) } } } @@ -1000,7 +1000,7 @@ mod eha { type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + fn flush(&mut self) -> Result<(), Self::Error> { async move { Ok(()) } } } @@ -1012,4 +1012,26 @@ mod eha { self.read(buffer) } } + + impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buffer) + } + } + + impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buffer) + } + + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + + fn flush(&mut self) -> Result<(), Self::Error> { + async move { Ok(()) } + } + } } diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index eb1472fa..ed4d5cf3 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,6 +1,6 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; @@ -28,11 +28,7 @@ static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); /// here provides a hook into determining whether it is. pub trait UsbSupply { fn is_usb_detected(&self) -> bool; - - type UsbPowerReadyFuture<'a>: Future> + 'a - where - Self: 'a; - fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_>; + async fn wait_power_ready(&mut self) -> Result<(), ()>; } pub struct Driver<'d, T: Instance, P: UsbSupply> { @@ -102,8 +98,7 @@ impl UsbSupply for PowerUsb { regs.usbregstatus.read().vbusdetect().is_vbus_present() } - type UsbPowerReadyFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> { + async fn wait_power_ready(&mut self) -> Result<(), ()> { poll_fn(move |cx| { POWER_WAKER.register(cx.waker()); let regs = unsafe { &*pac::POWER::ptr() }; @@ -116,6 +111,7 @@ impl UsbSupply for PowerUsb { Poll::Pending } }) + .await } } @@ -147,8 +143,7 @@ impl UsbSupply for &SignalledSupply { self.usb_detected.load(Ordering::Relaxed) } - type UsbPowerReadyFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> { + async fn wait_power_ready(&mut self) -> Result<(), ()> { poll_fn(move |cx| { POWER_WAKER.register(cx.waker()); @@ -160,6 +155,7 @@ impl UsbSupply for &SignalledSupply { Poll::Pending } }) + .await } } @@ -289,61 +285,52 @@ pub struct Bus<'d, T: Instance, P: UsbSupply> { } impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { - type EnableFuture<'a> = impl Future + 'a where Self: 'a; - type DisableFuture<'a> = impl Future + 'a where Self: 'a; - type PollFuture<'a> = impl Future + 'a where Self: 'a; - type RemoteWakeupFuture<'a> = impl Future> + 'a where Self: 'a; + async fn enable(&mut self) { + let regs = T::regs(); - fn enable(&mut self) -> Self::EnableFuture<'_> { - async move { - let regs = T::regs(); + errata::pre_enable(); - errata::pre_enable(); + regs.enable.write(|w| w.enable().enabled()); - regs.enable.write(|w| w.enable().enabled()); - - // Wait until the peripheral is ready. - regs.intenset.write(|w| w.usbevent().set_bit()); - poll_fn(|cx| { - BUS_WAKER.register(cx.waker()); - if regs.eventcause.read().ready().is_ready() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - regs.eventcause.write(|w| w.ready().clear_bit_by_one()); - - errata::post_enable(); - - unsafe { NVIC::unmask(pac::Interrupt::USBD) }; - - regs.intenset.write(|w| { - w.usbreset().set_bit(); - w.usbevent().set_bit(); - w.epdata().set_bit(); - w - }); - - if self.usb_supply.wait_power_ready().await.is_ok() { - // Enable the USB pullup, allowing enumeration. - regs.usbpullup.write(|w| w.connect().enabled()); - trace!("enabled"); + // Wait until the peripheral is ready. + regs.intenset.write(|w| w.usbevent().set_bit()); + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + if regs.eventcause.read().ready().is_ready() { + Poll::Ready(()) } else { - trace!("usb power not ready due to usb removal"); + Poll::Pending } + }) + .await; + regs.eventcause.write(|w| w.ready().clear_bit_by_one()); + + errata::post_enable(); + + unsafe { NVIC::unmask(pac::Interrupt::USBD) }; + + regs.intenset.write(|w| { + w.usbreset().set_bit(); + w.usbevent().set_bit(); + w.epdata().set_bit(); + w + }); + + if self.usb_supply.wait_power_ready().await.is_ok() { + // Enable the USB pullup, allowing enumeration. + regs.usbpullup.write(|w| w.connect().enabled()); + trace!("enabled"); + } else { + trace!("usb power not ready due to usb removal"); } } - fn disable(&mut self) -> Self::DisableFuture<'_> { - async move { - let regs = T::regs(); - regs.enable.write(|x| x.enable().disabled()); - } + async fn disable(&mut self) { + let regs = T::regs(); + regs.enable.write(|x| x.enable().disabled()); } - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { + async fn poll(&mut self) -> Event { poll_fn(move |cx| { BUS_WAKER.register(cx.waker()); let regs = T::regs(); @@ -401,6 +388,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { Poll::Pending }) + .await } #[inline] @@ -493,42 +481,40 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } #[inline] - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { - async move { - let regs = T::regs(); + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + let regs = T::regs(); - if regs.lowpower.read().lowpower().is_low_power() { - errata::pre_wakeup(); + if regs.lowpower.read().lowpower().is_low_power() { + errata::pre_wakeup(); - regs.lowpower.write(|w| w.lowpower().force_normal()); + regs.lowpower.write(|w| w.lowpower().force_normal()); - poll_fn(|cx| { - BUS_WAKER.register(cx.waker()); - let regs = T::regs(); - let r = regs.eventcause.read(); + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + let regs = T::regs(); + let r = regs.eventcause.read(); - if regs.events_usbreset.read().bits() != 0 { - Poll::Ready(()) - } else if r.resume().bit() { - Poll::Ready(()) - } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().allowed()); + if regs.events_usbreset.read().bits() != 0 { + Poll::Ready(()) + } else if r.resume().bit() { + Poll::Ready(()) + } else if r.usbwuallowed().bit() { + regs.eventcause.write(|w| w.usbwuallowed().allowed()); - regs.dpdmvalue.write(|w| w.state().resume()); - regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); + regs.dpdmvalue.write(|w| w.state().resume()); + regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; - errata::post_wakeup(); - } - - Ok(()) + errata::post_wakeup(); } + + Ok(()) } } @@ -594,9 +580,7 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { + async fn wait_enabled(&mut self) { let i = self.info.addr.index(); assert!(i != 0); @@ -608,6 +592,7 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir Poll::Pending } }) + .await } } @@ -712,34 +697,26 @@ unsafe fn write_dma(i: usize, buf: &[u8]) { } impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + async fn read(&mut self, buf: &mut [u8]) -> Result { + let i = self.info.addr.index(); + assert!(i != 0); - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let i = self.info.addr.index(); - assert!(i != 0); + self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - - unsafe { read_dma::(i, buf) } - } + unsafe { read_dma::(i, buf) } } } impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + let i = self.info.addr.index(); + assert!(i != 0); - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - let i = self.info.addr.index(); - assert!(i != 0); + self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; + unsafe { write_dma::(i, buf) } - unsafe { write_dma::(i, buf) } - - Ok(()) - } + Ok(()) } } @@ -749,136 +726,120 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - type SetupFuture<'a> = impl Future + 'a where Self: 'a; - type DataOutFuture<'a> = impl Future> + 'a where Self: 'a; - type DataInFuture<'a> = impl Future> + 'a where Self: 'a; - type AcceptFuture<'a> = impl Future + 'a where Self: 'a; - type RejectFuture<'a> = impl Future + 'a where Self: 'a; - fn max_packet_size(&self) -> usize { usize::from(self.max_packet_size) } - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { - async move { + async fn setup(&mut self) -> [u8; 8] { + let regs = T::regs(); + + // Reset shorts + regs.shorts.write(|w| w); + + // Wait for SETUP packet + regs.intenset.write(|w| w.ep0setup().set()); + poll_fn(|cx| { + EP0_WAKER.register(cx.waker()); let regs = T::regs(); + if regs.events_ep0setup.read().bits() != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; - // Reset shorts - regs.shorts.write(|w| w); + regs.events_ep0setup.reset(); - // Wait for SETUP packet - regs.intenset.write(|w| w.ep0setup().set()); - poll_fn(|cx| { - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0setup.read().bits() != 0 { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; + let mut buf = [0; 8]; + buf[0] = regs.bmrequesttype.read().bits() as u8; + buf[1] = regs.brequest.read().brequest().bits(); + buf[2] = regs.wvaluel.read().wvaluel().bits(); + buf[3] = regs.wvalueh.read().wvalueh().bits(); + buf[4] = regs.windexl.read().windexl().bits(); + buf[5] = regs.windexh.read().windexh().bits(); + buf[6] = regs.wlengthl.read().wlengthl().bits(); + buf[7] = regs.wlengthh.read().wlengthh().bits(); - regs.events_ep0setup.reset(); - - let mut buf = [0; 8]; - buf[0] = regs.bmrequesttype.read().bits() as u8; - buf[1] = regs.brequest.read().brequest().bits(); - buf[2] = regs.wvaluel.read().wvaluel().bits(); - buf[3] = regs.wvalueh.read().wvalueh().bits(); - buf[4] = regs.windexl.read().windexl().bits(); - buf[5] = regs.windexh.read().windexh().bits(); - buf[6] = regs.wlengthl.read().wlengthl().bits(); - buf[7] = regs.wlengthh.read().wlengthh().bits(); - - buf - } + buf } - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], _first: bool, _last: bool) -> Self::DataOutFuture<'a> { - async move { + async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { + let regs = T::regs(); + + regs.events_ep0datadone.reset(); + + // This starts a RX on EP0. events_ep0datadone notifies when done. + regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); + + // Wait until ready + regs.intenset.write(|w| { + w.usbreset().set(); + w.ep0setup().set(); + w.ep0datadone().set() + }); + poll_fn(|cx| { + EP0_WAKER.register(cx.waker()); let regs = T::regs(); + if regs.events_ep0datadone.read().bits() != 0 { + Poll::Ready(Ok(())) + } else if regs.events_usbreset.read().bits() != 0 { + trace!("aborted control data_out: usb reset"); + Poll::Ready(Err(EndpointError::Disabled)) + } else if regs.events_ep0setup.read().bits() != 0 { + trace!("aborted control data_out: received another SETUP"); + Poll::Ready(Err(EndpointError::Disabled)) + } else { + Poll::Pending + } + }) + .await?; - regs.events_ep0datadone.reset(); - - // This starts a RX on EP0. events_ep0datadone notifies when done. - regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); - - // Wait until ready - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() - }); - poll_fn(|cx| { - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { - Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { - trace!("aborted control data_out: usb reset"); - Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { - trace!("aborted control data_out: received another SETUP"); - Poll::Ready(Err(EndpointError::Disabled)) - } else { - Poll::Pending - } - }) - .await?; - - unsafe { read_dma::(0, buf) } - } + unsafe { read_dma::(0, buf) } } - fn data_in<'a>(&'a mut self, buf: &'a [u8], _first: bool, last: bool) -> Self::DataInFuture<'a> { - async move { + async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { + let regs = T::regs(); + regs.events_ep0datadone.reset(); + + regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); + + // This starts a TX on EP0. events_ep0datadone notifies when done. + unsafe { write_dma::(0, buf) } + + regs.intenset.write(|w| { + w.usbreset().set(); + w.ep0setup().set(); + w.ep0datadone().set() + }); + + poll_fn(|cx| { + cx.waker().wake_by_ref(); + EP0_WAKER.register(cx.waker()); let regs = T::regs(); - regs.events_ep0datadone.reset(); - - regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); - - // This starts a TX on EP0. events_ep0datadone notifies when done. - unsafe { write_dma::(0, buf) } - - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() - }); - - poll_fn(|cx| { - cx.waker().wake_by_ref(); - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { - Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { - trace!("aborted control data_in: usb reset"); - Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { - trace!("aborted control data_in: received another SETUP"); - Poll::Ready(Err(EndpointError::Disabled)) - } else { - Poll::Pending - } - }) - .await - } + if regs.events_ep0datadone.read().bits() != 0 { + Poll::Ready(Ok(())) + } else if regs.events_usbreset.read().bits() != 0 { + trace!("aborted control data_in: usb reset"); + Poll::Ready(Err(EndpointError::Disabled)) + } else if regs.events_ep0setup.read().bits() != 0 { + trace!("aborted control data_in: received another SETUP"); + Poll::Ready(Err(EndpointError::Disabled)) + } else { + Poll::Pending + } + }) + .await } - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { - async move { - let regs = T::regs(); - regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); - } + async fn accept(&mut self) { + let regs = T::regs(); + regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); } - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { - async move { - let regs = T::regs(); - regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); - } + async fn reject(&mut self) { + let regs = T::regs(); + regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); } } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 04b0c13c..770d8e25 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -53,7 +53,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.1", features = ["async"], optional = true } +embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } @@ -61,5 +61,5 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90 embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f79f592b..71390306 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -870,9 +870,6 @@ mod eh02 { mod eh1 { use core::convert::Infallible; - #[cfg(feature = "nightly")] - use futures::FutureExt; - use super::*; impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> { @@ -991,57 +988,57 @@ mod eh1 { #[cfg(feature = "nightly")] impl<'d, T: Pin> embedded_hal_async::digital::Wait for Flex<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) } } #[cfg(feature = "nightly")] impl<'d, T: Pin> embedded_hal_async::digital::Wait for Input<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) } } } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d6742f6a..e48e16d8 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -717,8 +717,6 @@ mod eh1 { } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod nightly { - use core::future::Future; - use embedded_hal_1::i2c::Operation; use embedded_hal_async::i2c::AddressMode; @@ -729,74 +727,55 @@ mod nightly { A: AddressMode + Into + 'static, T: Instance + 'd, { - type ReadFuture<'a> = impl Future> + 'a - where Self: 'a; - type WriteFuture<'a> = impl Future> + 'a - where Self: 'a; - type WriteReadFuture<'a> = impl Future> + 'a - where Self: 'a; - type TransactionFuture<'a, 'b> = impl Future> + 'a - where Self: 'a, 'b: 'a; - - fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - Self::setup(addr)?; - self.read_async_internal(buffer, false, true).await - } + Self::setup(addr)?; + self.read_async_internal(read, false, true).await } - fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> { + async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - Self::setup(addr)?; - self.write_async_internal(write.iter().copied(), true).await - } + Self::setup(addr)?; + self.write_async_internal(write.iter().copied(), true).await } - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: A, - bytes: &'a [u8], - buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { + write: &'a [u8], + read: &'a mut [u8], + ) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - Self::setup(addr)?; - self.write_async_internal(bytes.iter().cloned(), false).await?; - self.read_async_internal(buffer, false, true).await - } + Self::setup(addr)?; + self.write_async_internal(write.iter().cloned(), false).await?; + self.read_async_internal(read, false, true).await } - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: A, operations: &'a mut [Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - let mut iterator = operations.iter_mut(); + let mut iterator = operations.iter_mut(); - while let Some(op) = iterator.next() { - let last = iterator.len() == 0; + while let Some(op) = iterator.next() { + let last = iterator.len() == 0; - match op { - Operation::Read(buffer) => { - Self::setup(addr)?; - self.read_async_internal(buffer, false, last).await?; - } - Operation::Write(buffer) => { - Self::setup(addr)?; - self.write_async_internal(buffer.into_iter().cloned(), last).await?; - } + match op { + Operation::Read(buffer) => { + Self::setup(addr)?; + self.read_async_internal(buffer, false, last).await?; + } + Operation::Write(buffer) => { + Self::setup(addr)?; + self.write_async_internal(buffer.into_iter().cloned(), last).await?; } } - Ok(()) } + Ok(()) } } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 6c91b1ad..e5b07c90 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 754e2dd3..2b7a818d 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -554,45 +554,33 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - use super::*; impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Async> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(data) + async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.write(words).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spi<'d, T, Async> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(data) + async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.read(words).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> { - self.transfer(rx, tx) + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + self.transfer(read, write).await } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { - self.transfer_in_place(words) + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { + self.transfer_in_place(words).await } } } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 4f0a5553..fa466c8a 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,4 +1,4 @@ -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; @@ -355,11 +355,7 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + async fn read(&mut self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); @@ -372,15 +368,12 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { res }) + .await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + async fn read(&mut self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); @@ -393,21 +386,19 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { res }) + .await } } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); state.rx.fill_buf(cx.waker()) }) }) + .await } fn consume(&mut self, amt: usize) { @@ -419,17 +410,14 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); state.fill_buf(cx.waker()) }) }) + .await } fn consume(&mut self, amt: usize) { @@ -441,11 +429,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { @@ -453,23 +437,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } poll }) + .await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { @@ -477,13 +454,10 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> } poll }) + .await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))).await } } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 6dc90b98..32fc2632 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,4 +1,4 @@ -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::slice; use core::sync::atomic::Ordering; @@ -352,9 +352,7 @@ pub struct Bus<'d, T: Instance> { } impl<'d, T: Instance> driver::Bus for Bus<'d, T> { - type PollFuture<'a> = impl Future + 'a where Self: 'a; - - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { + async fn poll(&mut self) -> Event { poll_fn(move |cx| unsafe { BUS_WAKER.register(cx.waker()); @@ -406,6 +404,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { }); Poll::Pending }) + .await } #[inline] @@ -456,22 +455,12 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } } - type EnableFuture<'a> = impl Future + 'a where Self: 'a; + async fn enable(&mut self) {} - fn enable(&mut self) -> Self::EnableFuture<'_> { - async move {} - } + async fn disable(&mut self) {} - type DisableFuture<'a> = impl Future + 'a where Self: 'a; - - fn disable(&mut self) -> Self::DisableFuture<'_> { - async move {} - } - - type RemoteWakeupFuture<'a> = impl Future> + 'a where Self: 'a; - - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { - async move { Err(Unsupported) } + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + Err(Unsupported) } } @@ -515,24 +504,20 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled IN WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_IN_WAKERS[index].register(cx.waker()); - let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; - if val.enable() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - trace!("wait_enabled IN OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled IN WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_IN_WAKERS[index].register(cx.waker()); + let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; + if val.enable() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + trace!("wait_enabled IN OK"); } } @@ -541,117 +526,105 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled OUT WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let val = unsafe { T::dpram().ep_out_control(self.info.addr.index() - 1).read() }; - if val.enable() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - trace!("wait_enabled OUT OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled OUT WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); + let val = unsafe { T::dpram().ep_out_control(self.info.addr.index() - 1).read() }; + if val.enable() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + trace!("wait_enabled OUT OK"); } } impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - trace!("READ WAITING, buf.len() = {}", buf.len()); - let index = self.info.addr.index(); - let val = poll_fn(|cx| unsafe { - EP_OUT_WAKERS[index].register(cx.waker()); - let val = T::dpram().ep_out_buffer_control(index).read(); - if val.available(0) { - Poll::Pending - } else { - Poll::Ready(val) - } - }) - .await; - - let rx_len = val.length(0) as usize; - if rx_len > buf.len() { - return Err(EndpointError::BufferOverflow); + async fn read(&mut self, buf: &mut [u8]) -> Result { + trace!("READ WAITING, buf.len() = {}", buf.len()); + let index = self.info.addr.index(); + let val = poll_fn(|cx| unsafe { + EP_OUT_WAKERS[index].register(cx.waker()); + let val = T::dpram().ep_out_buffer_control(index).read(); + if val.available(0) { + Poll::Pending + } else { + Poll::Ready(val) } - self.buf.read(&mut buf[..rx_len]); + }) + .await; - trace!("READ OK, rx_len = {}", rx_len); - - unsafe { - let pid = !val.pid(0); - T::dpram().ep_out_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, self.info.max_packet_size); - }); - cortex_m::asm::delay(12); - T::dpram().ep_out_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, self.info.max_packet_size); - w.set_available(0, true); - }); - } - - Ok(rx_len) + let rx_len = val.length(0) as usize; + if rx_len > buf.len() { + return Err(EndpointError::BufferOverflow); } + self.buf.read(&mut buf[..rx_len]); + + trace!("READ OK, rx_len = {}", rx_len); + + unsafe { + let pid = !val.pid(0); + T::dpram().ep_out_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, self.info.max_packet_size); + }); + cortex_m::asm::delay(12); + T::dpram().ep_out_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, self.info.max_packet_size); + w.set_available(0, true); + }); + } + + Ok(rx_len) } } impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - if buf.len() > self.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); - } - - trace!("WRITE WAITING"); - - let index = self.info.addr.index(); - let val = poll_fn(|cx| unsafe { - EP_IN_WAKERS[index].register(cx.waker()); - let val = T::dpram().ep_in_buffer_control(index).read(); - if val.available(0) { - Poll::Pending - } else { - Poll::Ready(val) - } - }) - .await; - - self.buf.write(buf); - - unsafe { - let pid = !val.pid(0); - T::dpram().ep_in_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, buf.len() as _); - w.set_full(0, true); - }); - cortex_m::asm::delay(12); - T::dpram().ep_in_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, buf.len() as _); - w.set_full(0, true); - w.set_available(0, true); - }); - } - - trace!("WRITE OK"); - - Ok(()) + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if buf.len() > self.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); } + + trace!("WRITE WAITING"); + + let index = self.info.addr.index(); + let val = poll_fn(|cx| unsafe { + EP_IN_WAKERS[index].register(cx.waker()); + let val = T::dpram().ep_in_buffer_control(index).read(); + if val.available(0) { + Poll::Pending + } else { + Poll::Ready(val) + } + }) + .await; + + self.buf.write(buf); + + unsafe { + let pid = !val.pid(0); + T::dpram().ep_in_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, buf.len() as _); + w.set_full(0, true); + }); + cortex_m::asm::delay(12); + T::dpram().ep_in_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, buf.len() as _); + w.set_full(0, true); + w.set_available(0, true); + }); + } + + trace!("WRITE OK"); + + Ok(()) } } @@ -661,199 +634,183 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - type SetupFuture<'a> = impl Future + 'a where Self: 'a; - type DataOutFuture<'a> = impl Future> + 'a where Self: 'a; - type DataInFuture<'a> = impl Future> + 'a where Self: 'a; - type AcceptFuture<'a> = impl Future + 'a where Self: 'a; - type RejectFuture<'a> = impl Future + 'a where Self: 'a; - fn max_packet_size(&self) -> usize { 64 } - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { - async move { - loop { - trace!("SETUP read waiting"); - let regs = T::regs(); - unsafe { regs.inte().write_set(|w| w.set_setup_req(true)) }; - - poll_fn(|cx| unsafe { - EP_OUT_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if regs.sie_status().read().setup_rec() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - let mut buf = [0; 8]; - EndpointBuffer::::new(0, 8).read(&mut buf); - - let regs = T::regs(); - unsafe { - regs.sie_status().write(|w| w.set_setup_rec(true)); - - // set PID to 0, so (after toggling) first DATA is PID 1 - T::dpram().ep_in_buffer_control(0).write(|w| w.set_pid(0, false)); - T::dpram().ep_out_buffer_control(0).write(|w| w.set_pid(0, false)); - } - - trace!("SETUP read ok"); - return buf; - } - } - } - - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], _first: bool, _last: bool) -> Self::DataOutFuture<'a> { - async move { - unsafe { - let bufcontrol = T::dpram().ep_out_buffer_control(0); - let pid = !bufcontrol.read().pid(0); - bufcontrol.write(|w| { - w.set_length(0, self.max_packet_size); - w.set_pid(0, pid); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, self.max_packet_size); - w.set_pid(0, pid); - w.set_available(0, true); - }); - } - - trace!("control: data_out len={} first={} last={}", buf.len(), _first, _last); - let val = poll_fn(|cx| unsafe { - EP_OUT_WAKERS[0].register(cx.waker()); - let val = T::dpram().ep_out_buffer_control(0).read(); - if val.available(0) { - Poll::Pending - } else { - Poll::Ready(val) - } - }) - .await; - - let rx_len = val.length(0) as _; - trace!("control data_out DONE, rx_len = {}", rx_len); - - if rx_len > buf.len() { - return Err(EndpointError::BufferOverflow); - } - EndpointBuffer::::new(0x100, 64).read(&mut buf[..rx_len]); - - Ok(rx_len) - } - } - - fn data_in<'a>(&'a mut self, buf: &'a [u8], _first: bool, _last: bool) -> Self::DataInFuture<'a> { - async move { - trace!("control: data_in len={} first={} last={}", buf.len(), _first, _last); - - if buf.len() > 64 { - return Err(EndpointError::BufferOverflow); - } - EndpointBuffer::::new(0x100, 64).write(buf); - - unsafe { - let bufcontrol = T::dpram().ep_in_buffer_control(0); - let pid = !bufcontrol.read().pid(0); - bufcontrol.write(|w| { - w.set_length(0, buf.len() as _); - w.set_pid(0, pid); - w.set_full(0, true); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, buf.len() as _); - w.set_pid(0, pid); - w.set_full(0, true); - w.set_available(0, true); - }); - } + async fn setup<'a>(&'a mut self) -> [u8; 8] { + loop { + trace!("SETUP read waiting"); + let regs = T::regs(); + unsafe { regs.inte().write_set(|w| w.set_setup_req(true)) }; poll_fn(|cx| unsafe { - EP_IN_WAKERS[0].register(cx.waker()); - let bufcontrol = T::dpram().ep_in_buffer_control(0); - if bufcontrol.read().available(0) { - Poll::Pending - } else { + EP_OUT_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if regs.sie_status().read().setup_rec() { Poll::Ready(()) + } else { + Poll::Pending } }) .await; - trace!("control: data_in DONE"); - if _last { - // prepare status phase right away. - unsafe { - let bufcontrol = T::dpram().ep_out_buffer_control(0); - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - w.set_available(0, true); - }); - } - } - - Ok(()) - } - } - - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { - async move { - trace!("control: accept"); - - let bufcontrol = T::dpram().ep_in_buffer_control(0); - unsafe { - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - w.set_full(0, true); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - w.set_full(0, true); - w.set_available(0, true); - }); - } - - // wait for completion before returning, needed so - // set_address() doesn't happen early. - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - if unsafe { bufcontrol.read().available(0) } { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - } - } - - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { - async move { - trace!("control: reject"); + let mut buf = [0; 8]; + EndpointBuffer::::new(0, 8).read(&mut buf); let regs = T::regs(); unsafe { - regs.ep_stall_arm().write_set(|w| { - w.set_ep0_in(true); - w.set_ep0_out(true); - }); - T::dpram().ep_out_buffer_control(0).write(|w| w.set_stall(true)); - T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); + regs.sie_status().write(|w| w.set_setup_rec(true)); + + // set PID to 0, so (after toggling) first DATA is PID 1 + T::dpram().ep_in_buffer_control(0).write(|w| w.set_pid(0, false)); + T::dpram().ep_out_buffer_control(0).write(|w| w.set_pid(0, false)); } + + trace!("SETUP read ok"); + return buf; + } + } + + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { + unsafe { + let bufcontrol = T::dpram().ep_out_buffer_control(0); + let pid = !bufcontrol.read().pid(0); + bufcontrol.write(|w| { + w.set_length(0, self.max_packet_size); + w.set_pid(0, pid); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, self.max_packet_size); + w.set_pid(0, pid); + w.set_available(0, true); + }); + } + + trace!("control: data_out len={} first={} last={}", buf.len(), first, last); + let val = poll_fn(|cx| unsafe { + EP_OUT_WAKERS[0].register(cx.waker()); + let val = T::dpram().ep_out_buffer_control(0).read(); + if val.available(0) { + Poll::Pending + } else { + Poll::Ready(val) + } + }) + .await; + + let rx_len = val.length(0) as _; + trace!("control data_out DONE, rx_len = {}", rx_len); + + if rx_len > buf.len() { + return Err(EndpointError::BufferOverflow); + } + EndpointBuffer::::new(0x100, 64).read(&mut buf[..rx_len]); + + Ok(rx_len) + } + + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> { + trace!("control: data_in len={} first={} last={}", data.len(), first, last); + + if data.len() > 64 { + return Err(EndpointError::BufferOverflow); + } + EndpointBuffer::::new(0x100, 64).write(data); + + unsafe { + let bufcontrol = T::dpram().ep_in_buffer_control(0); + let pid = !bufcontrol.read().pid(0); + bufcontrol.write(|w| { + w.set_length(0, data.len() as _); + w.set_pid(0, pid); + w.set_full(0, true); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, data.len() as _); + w.set_pid(0, pid); + w.set_full(0, true); + w.set_available(0, true); + }); + } + + poll_fn(|cx| unsafe { + EP_IN_WAKERS[0].register(cx.waker()); + let bufcontrol = T::dpram().ep_in_buffer_control(0); + if bufcontrol.read().available(0) { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + trace!("control: data_in DONE"); + + if last { + // prepare status phase right away. + unsafe { + let bufcontrol = T::dpram().ep_out_buffer_control(0); + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + w.set_available(0, true); + }); + } + } + + Ok(()) + } + + async fn accept(&mut self) { + trace!("control: accept"); + + let bufcontrol = T::dpram().ep_in_buffer_control(0); + unsafe { + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + w.set_full(0, true); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + w.set_full(0, true); + w.set_available(0, true); + }); + } + + // wait for completion before returning, needed so + // set_address() doesn't happen early. + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + if unsafe { bufcontrol.read().available(0) } { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + } + + async fn reject(&mut self) { + trace!("control: reject"); + + let regs = T::regs(); + unsafe { + regs.ep_stall_arm().write_set(|w| { + w.set_ep0_in(true); + w.set_ep0_out(true); + }); + T::dpram().ep_out_buffer_control(0).write(|w| w.set_stall(true)); + T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); } } } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6b00518a..b7f718c5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,7 +44,7 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-storage = "0.3.0" @@ -67,7 +67,7 @@ nb = "1.0.0" stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" -embedded-io = { version = "0.3.1", features = ["async"], optional = true } +embedded-io = { version = "0.4.0", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index dca99185..f9078581 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -167,39 +167,33 @@ mod eh1 { } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use futures::FutureExt; use super::*; impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) } } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index aa4e6bb0..47dc7d2a 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1048,43 +1048,35 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - use super::super::{RxDma, TxDma}; use super::*; impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(address, buffer) + async fn read<'a>(&'a mut self, address: u8, read: &'a mut [u8]) -> Result<(), Self::Error> { + self.read(address, read).await } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(address, bytes) + async fn write<'a>(&'a mut self, address: u8, write: &'a [u8]) -> Result<(), Self::Error> { + self.write(address, write).await } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, - bytes: &'a [u8], - buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - self.write_read(address, bytes, buffer) + write: &'a [u8], + read: &'a mut [u8], + ) -> Result<(), Self::Error> { + self.write_read(address, write, read).await } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], + ) -> Result<(), Self::Error> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index bcf2feee..d7443eac 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,9 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 39642778..17198fc2 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -885,46 +885,34 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - use super::*; impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } impl<'d, T: Instance, Tx: TxDma, Rx, W: Word> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Tx, Rx> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [W]) -> Self::WriteFuture<'a> { - self.write(data) + async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { + self.write(words).await } } impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBusRead for Spi<'d, T, Tx, Rx> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, data: &'a mut [W]) -> Self::ReadFuture<'a> { - self.read(data) + async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { + self.read(words).await } } impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, rx: &'a mut [W], tx: &'a [W]) -> Self::TransferFuture<'a> { - self.transfer(rx, tx) + async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> { + self.transfer(read, write).await } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a> { - self.transfer_in_place(words) + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> { + self.transfer_in_place(words).await } } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 0a6d6e14..acd96d7c 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; @@ -339,32 +339,20 @@ impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { } impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await } } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.inner_read(buf).await } } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -373,12 +361,8 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -387,37 +371,21 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<' } impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner_flush().await } } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_flush().await } } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 2654f156..0ba06cce 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -1,6 +1,6 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; @@ -429,9 +429,7 @@ pub struct Bus<'d, T: Instance> { } impl<'d, T: Instance> driver::Bus for Bus<'d, T> { - type PollFuture<'a> = impl Future + 'a where Self: 'a; - - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { + async fn poll(&mut self) -> Event { poll_fn(move |cx| unsafe { BUS_WAKER.register(cx.waker()); @@ -488,6 +486,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { return Poll::Ready(Event::PowerDetected); } }) + .await } #[inline] @@ -598,22 +597,11 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trace!("EPR after: {:04x}", unsafe { reg.read() }.0); } - type EnableFuture<'a> = impl Future + 'a where Self: 'a; + async fn enable(&mut self) {} + async fn disable(&mut self) {} - fn enable(&mut self) -> Self::EnableFuture<'_> { - async move {} - } - - type DisableFuture<'a> = impl Future + 'a where Self: 'a; - - fn disable(&mut self) -> Self::DisableFuture<'_> { - async move {} - } - - type RemoteWakeupFuture<'a> = impl Future> + 'a where Self: 'a; - - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { - async move { Err(Unsupported) } + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + Err(Unsupported) } } @@ -676,24 +664,20 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled OUT WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - trace!("wait_enabled OUT OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled OUT WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + trace!("wait_enabled OUT OK"); } } @@ -702,116 +686,104 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled OUT WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - trace!("wait_enabled OUT OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled OUT WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + trace!("wait_enabled OUT OK"); } } impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - trace!("READ WAITING, buf.len() = {}", buf.len()); - let index = self.info.addr.index(); - let stat = poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - let stat = unsafe { regs.epr(index).read() }.stat_rx(); - if matches!(stat, Stat::NAK | Stat::DISABLED) { - Poll::Ready(stat) - } else { - Poll::Pending - } - }) - .await; - - if stat == Stat::DISABLED { - return Err(EndpointError::Disabled); - } - - let rx_len = self.read_data(buf)?; - + async fn read(&mut self, buf: &mut [u8]) -> Result { + trace!("READ WAITING, buf.len() = {}", buf.len()); + let index = self.info.addr.index(); + let stat = poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); let regs = T::regs(); - unsafe { - regs.epr(index).write(|w| { - w.set_ep_type(convert_type(self.info.ep_type)); - w.set_ea(self.info.addr.index() as _); - w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); - w.set_stat_tx(Stat(0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - }; - trace!("READ OK, rx_len = {}", rx_len); + let stat = unsafe { regs.epr(index).read() }.stat_rx(); + if matches!(stat, Stat::NAK | Stat::DISABLED) { + Poll::Ready(stat) + } else { + Poll::Pending + } + }) + .await; - Ok(rx_len) + if stat == Stat::DISABLED { + return Err(EndpointError::Disabled); } + + let rx_len = self.read_data(buf)?; + + let regs = T::regs(); + unsafe { + regs.epr(index).write(|w| { + w.set_ep_type(convert_type(self.info.ep_type)); + w.set_ea(self.info.addr.index() as _); + w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_stat_tx(Stat(0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }) + }; + trace!("READ OK, rx_len = {}", rx_len); + + Ok(rx_len) } } impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - if buf.len() > self.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); - } - - let index = self.info.addr.index(); - - trace!("WRITE WAITING"); - let stat = poll_fn(|cx| { - EP_IN_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - let stat = unsafe { regs.epr(index).read() }.stat_tx(); - if matches!(stat, Stat::NAK | Stat::DISABLED) { - Poll::Ready(stat) - } else { - Poll::Pending - } - }) - .await; - - if stat == Stat::DISABLED { - return Err(EndpointError::Disabled); - } - - self.write_data(buf); - - let regs = T::regs(); - unsafe { - regs.epr(index).write(|w| { - w.set_ep_type(convert_type(self.info.ep_type)); - w.set_ea(self.info.addr.index() as _); - w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); - w.set_stat_rx(Stat(0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - }; - - trace!("WRITE OK"); - - Ok(()) + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if buf.len() > self.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); } + + let index = self.info.addr.index(); + + trace!("WRITE WAITING"); + let stat = poll_fn(|cx| { + EP_IN_WAKERS[index].register(cx.waker()); + let regs = T::regs(); + let stat = unsafe { regs.epr(index).read() }.stat_tx(); + if matches!(stat, Stat::NAK | Stat::DISABLED) { + Poll::Ready(stat) + } else { + Poll::Pending + } + }) + .await; + + if stat == Stat::DISABLED { + return Err(EndpointError::Disabled); + } + + self.write_data(buf); + + let regs = T::regs(); + unsafe { + regs.epr(index).write(|w| { + w.set_ep_type(convert_type(self.info.ep_type)); + w.set_ea(self.info.addr.index() as _); + w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_stat_rx(Stat(0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }) + }; + + trace!("WRITE OK"); + + Ok(()) } } @@ -823,84 +795,16 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - type SetupFuture<'a> = impl Future + 'a where Self: 'a; - type DataOutFuture<'a> = impl Future> + 'a where Self: 'a; - type DataInFuture<'a> = impl Future> + 'a where Self: 'a; - type AcceptFuture<'a> = impl Future + 'a where Self: 'a; - type RejectFuture<'a> = impl Future + 'a where Self: 'a; - fn max_packet_size(&self) -> usize { usize::from(self.max_packet_size) } - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { - async move { - loop { - trace!("SETUP read waiting"); - poll_fn(|cx| { - EP_OUT_WAKERS[0].register(cx.waker()); - if EP0_SETUP.load(Ordering::Relaxed) { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - let mut buf = [0; 8]; - let rx_len = self.ep_out.read_data(&mut buf); - if rx_len != Ok(8) { - trace!("SETUP read failed: {:?}", rx_len); - continue; - } - - EP0_SETUP.store(false, Ordering::Relaxed); - - trace!("SETUP read ok"); - return buf; - } - } - } - - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a> { - async move { - let regs = T::regs(); - - // When a SETUP is received, Stat/Stat is set to NAK. - // On first transfer, we must set Stat=VALID, to get the OUT data stage. - // We want Stat=STALL so that the host gets a STALL if it switches to the status - // stage too soon, except in the last transfer we set Stat=NAK so that it waits - // for the status stage, which we will ACK or STALL later. - if first || last { - let mut stat_rx = 0; - let mut stat_tx = 0; - if first { - // change NAK -> VALID - stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0; - stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0; - } - if last { - // change STALL -> VALID - stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; - } - // Note: if this is the first AND last transfer, the above effectively - // changes stat_tx like NAK -> NAK, so noop. - unsafe { - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(stat_rx)); - w.set_stat_tx(Stat(stat_tx)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - } - } - - trace!("data_out WAITING, buf.len() = {}", buf.len()); + async fn setup<'a>(&'a mut self) -> [u8; 8] { + loop { + trace!("SETUP read waiting"); poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK { + if EP0_SETUP.load(Ordering::Relaxed) { Poll::Ready(()) } else { Poll::Pending @@ -908,157 +812,209 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }) .await; - if EP0_SETUP.load(Ordering::Relaxed) { - trace!("received another SETUP, aborting data_out."); - return Err(EndpointError::Disabled); + let mut buf = [0; 8]; + let rx_len = self.ep_out.read_data(&mut buf); + if rx_len != Ok(8) { + trace!("SETUP read failed: {:?}", rx_len); + continue; } - let rx_len = self.ep_out.read_data(buf)?; + EP0_SETUP.store(false, Ordering::Relaxed); + trace!("SETUP read ok"); + return buf; + } + } + + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { + let regs = T::regs(); + + // When a SETUP is received, Stat/Stat is set to NAK. + // On first transfer, we must set Stat=VALID, to get the OUT data stage. + // We want Stat=STALL so that the host gets a STALL if it switches to the status + // stage too soon, except in the last transfer we set Stat=NAK so that it waits + // for the status stage, which we will ACK or STALL later. + if first || last { + let mut stat_rx = 0; + let mut stat_tx = 0; + if first { + // change NAK -> VALID + stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0; + stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0; + } + if last { + // change STALL -> VALID + stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; + } + // Note: if this is the first AND last transfer, the above effectively + // changes stat_tx like NAK -> NAK, so noop. unsafe { regs.epr(0).write(|w| { w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(match last { - // If last, set STAT_RX=STALL. - true => Stat::NAK.0 ^ Stat::STALL.0, - // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. - false => Stat::NAK.0 ^ Stat::VALID.0, - })); + w.set_stat_rx(Stat(stat_rx)); + w.set_stat_tx(Stat(stat_tx)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }) - }; - - Ok(rx_len) + } } + + trace!("data_out WAITING, buf.len() = {}", buf.len()); + poll_fn(|cx| { + EP_OUT_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + if EP0_SETUP.load(Ordering::Relaxed) { + trace!("received another SETUP, aborting data_out."); + return Err(EndpointError::Disabled); + } + + let rx_len = self.ep_out.read_data(buf)?; + + unsafe { + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat(match last { + // If last, set STAT_RX=STALL. + true => Stat::NAK.0 ^ Stat::STALL.0, + // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. + false => Stat::NAK.0 ^ Stat::VALID.0, + })); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }) + }; + + Ok(rx_len) } - fn data_in<'a>(&'a mut self, buf: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a> { - async move { - trace!("control: data_in"); + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> { + trace!("control: data_in"); - if buf.len() > self.ep_in.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); + if data.len() > self.ep_in.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + let regs = T::regs(); + + // When a SETUP is received, Stat is set to NAK. + // We want it to be STALL in non-last transfers. + // We want it to be VALID in last transfer, so the HW does the status stage. + if first || last { + let mut stat_rx = 0; + if first { + // change NAK -> STALL + stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; } - - let regs = T::regs(); - - // When a SETUP is received, Stat is set to NAK. - // We want it to be STALL in non-last transfers. - // We want it to be VALID in last transfer, so the HW does the status stage. - if first || last { - let mut stat_rx = 0; - if first { - // change NAK -> STALL - stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; - } - if last { - // change STALL -> VALID - stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; - } - // Note: if this is the first AND last transfer, the above effectively - // does a change of NAK -> VALID. - unsafe { - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(stat_rx)); - w.set_ep_kind(last); // set OUT_STATUS if last. - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - } + if last { + // change STALL -> VALID + stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; } - - trace!("WRITE WAITING"); - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - EP_OUT_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - if EP0_SETUP.load(Ordering::Relaxed) { - trace!("received another SETUP, aborting data_in."); - return Err(EndpointError::Disabled); - } - - self.ep_in.write_data(buf); - - let regs = T::regs(); + // Note: if this is the first AND last transfer, the above effectively + // does a change of NAK -> VALID. unsafe { regs.epr(0).write(|w| { w.set_ep_type(EpType::CONTROL); - w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_stat_rx(Stat(stat_rx)); w.set_ep_kind(last); // set OUT_STATUS if last. w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }) - }; - - trace!("WRITE OK"); - - Ok(()) - } - } - - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { - async move { - let regs = T::regs(); - trace!("control: accept"); - - self.ep_in.write_data(&[]); - - // Set OUT=stall, IN=accept - unsafe { - let epr = regs.epr(0).read(); - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); - w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }); } - trace!("control: accept WAITING"); + } - // Wait is needed, so that we don't set the address too soon, breaking the status stage. - // (embassy-usb sets the address after accept() returns) - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { - Poll::Ready(()) - } else { - Poll::Pending - } + trace!("WRITE WAITING"); + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + EP_OUT_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + if EP0_SETUP.load(Ordering::Relaxed) { + trace!("received another SETUP, aborting data_in."); + return Err(EndpointError::Disabled); + } + + self.ep_in.write_data(data); + + let regs = T::regs(); + unsafe { + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_ep_kind(last); // set OUT_STATUS if last. + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear }) - .await; + }; - trace!("control: accept OK"); - } + trace!("WRITE OK"); + + Ok(()) } - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { - async move { - let regs = T::regs(); - trace!("control: reject"); + async fn accept(&mut self) { + let regs = T::regs(); + trace!("control: accept"); - // Set IN+OUT to stall - unsafe { - let epr = regs.epr(0).read(); - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); - w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }); + self.ep_in.write_data(&[]); + + // Set OUT=stall, IN=accept + unsafe { + let epr = regs.epr(0).read(); + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); + w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }); + } + trace!("control: accept WAITING"); + + // Wait is needed, so that we don't set the address too soon, breaking the status stage. + // (embassy-usb sets the address after accept() returns) + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { + Poll::Ready(()) + } else { + Poll::Pending } + }) + .await; + + trace!("control: accept OK"); + } + + async fn reject(&mut self) { + let regs = T::regs(); + trace!("control: reject"); + + // Set IN+OUT to stall + unsafe { + let epr = regs.epr(0).read(); + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); + w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }); } } } diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index b7fe1643..1eeb94c9 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -35,7 +35,7 @@ atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.3.1" +embedded-io = "0.4.0" [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 9487003c..5701ab35 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -134,7 +134,7 @@ log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-sync = { version = "0.1", path = "../embassy-sync" } diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 931e9c31..85e9267d 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] - -use core::future::Future; +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] /// 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 @@ -155,27 +155,14 @@ pub trait Driver<'a> { } pub trait Bus { - type EnableFuture<'a>: Future + 'a - where - Self: 'a; - type DisableFuture<'a>: Future + 'a - where - Self: 'a; - type PollFuture<'a>: Future + 'a - where - Self: 'a; - type RemoteWakeupFuture<'a>: Future> + 'a - where - Self: 'a; - /// Enables the USB peripheral. Soon after enabling the device will be reset, so /// there is no need to perform a USB reset in this method. - fn enable(&mut self) -> Self::EnableFuture<'_>; + async fn enable(&mut self); /// Disables and powers down the USB peripheral. - fn disable(&mut self) -> Self::DisableFuture<'_>; + async fn disable(&mut self); - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>; + async fn poll(&mut self) -> Event; /// Sets the device USB address to `addr`. fn set_address(&mut self, addr: u8); @@ -209,85 +196,57 @@ pub trait Bus { /// /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support /// remote wakeup or it has not been enabled at creation time. - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_>; + async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } pub trait Endpoint { - type WaitEnabledFuture<'a>: Future + 'a - where - Self: 'a; - /// Get the endpoint address fn info(&self) -> &EndpointInfo; /// Waits for the endpoint to be enabled. - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>; + async fn wait_enabled(&mut self); } pub trait EndpointOut: Endpoint { - type ReadFuture<'a>: Future> + 'a - where - Self: 'a; - /// Reads a single packet of data from the endpoint, and returns the actual length of /// the packet. /// /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; + async fn read(&mut self, buf: &mut [u8]) -> Result; } pub trait ControlPipe { - type SetupFuture<'a>: Future + 'a - where - Self: 'a; - type DataOutFuture<'a>: Future> + 'a - where - Self: 'a; - type DataInFuture<'a>: Future> + 'a - where - Self: 'a; - type AcceptFuture<'a>: Future + 'a - where - Self: 'a; - type RejectFuture<'a>: Future + 'a - where - Self: 'a; - /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize; /// Reads a single setup packet from the endpoint. - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a>; + async fn setup<'a>(&'a mut self) -> [u8; 8]; /// Reads a DATA OUT packet into `buf` in response to a control write request. /// /// Must be called after `setup()` for requests with `direction` of `Out` /// and `length` greater than zero. - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a>; + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result; /// Sends a DATA IN packet with `data` in response to a control read request. /// /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. - fn data_in<'a>(&'a mut self, data: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a>; + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; /// Accepts a control request. /// /// Causes the STATUS packet for the current request to be ACKed. - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a>; + async fn accept(&mut self); /// Rejects a control request. /// /// Sets a STALL condition on the pipe to indicate an error. - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a>; + async fn reject(&mut self); } pub trait EndpointIn: Endpoint { - type WriteFuture<'a>: Future> + 'a - where - Self: 'a; - /// Writes a single packet of data to the endpoint. - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; } #[derive(Copy, Clone, Eq, PartialEq, Debug)] diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index c633f82f..64dbc663 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -17,7 +17,7 @@ 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 } -embedded-io = "0.3.1" +embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } @@ -34,4 +34,4 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" -serde = { version = "1.0.136", default-features = false } +serde = { version = "1.0.136", default-features = false } \ No newline at end of file diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 25022886..364f738d 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -29,8 +29,8 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.3" } -embedded-io = { version = "0.3.1", features = ["async", "defmt"] } +embedded-hal-async = "0.2.0-alpha.0" +embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" log = "0.4" diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 79025838..41680f8f 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } -embedded-io = { version = "0.3.1", features = ["async", "std", "futures"] } +embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index b05457ea..594b6161 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = "0.3" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-io = "0.3.1" +embedded-io = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index b14afd2f..caabe068 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,7 +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", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0dccff6e..de945999 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,7 +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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" @@ -19,8 +19,8 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } -embedded-nal-async = "0.2.0" +embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-nal-async = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 8b00773b..f5fd18f1 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -22,7 +22,7 @@ defmt = "0.3" defmt-rtt = "0.3" embedded-storage = "0.3.0" -embedded-io = "0.3.1" +embedded-io = "0.4.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 83d456b2..9092d85c 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -20,7 +20,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 848723f8..376e9e51 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -26,5 +26,5 @@ embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } static_cell = "1.0" diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 3d704011..1cf1078d 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -21,8 +21,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" - -#[patch.crates-io] -#defmt = { git="https://github.com/knurling-rs/defmt.git" } -#defmt-rtt = { git="https://github.com/knurling-rs/defmt.git" } - diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c05aac3f..55539405 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-10-25" +channel = "nightly-2022-11-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 069b7fb8..a6102b27 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -18,10 +18,10 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } 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.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } embedded-storage = { version = "0.3" } [profile.dev] diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 602c1fb5..f74a3c70 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From 7b838d03369f94e09d652982f994c5013e81457e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 7 Nov 2022 00:27:21 +0100 Subject: [PATCH 172/374] rp/uart: use lockfree ringbuffer. This gets rid of another PeripheralMutex usage. --- embassy-hal-common/src/atomic_ring_buffer.rs | 331 +++++++++ embassy-hal-common/src/lib.rs | 1 + embassy-rp/Cargo.toml | 2 +- embassy-rp/src/uart/buffered.rs | 706 ++++++++++--------- embassy-rp/src/uart/mod.rs | 91 ++- tests/rp/src/bin/uart_buffered.rs | 13 +- 6 files changed, 758 insertions(+), 386 deletions(-) create mode 100644 embassy-hal-common/src/atomic_ring_buffer.rs diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs new file mode 100644 index 00000000..c5e44430 --- /dev/null +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -0,0 +1,331 @@ +use core::slice; +use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; + +/// Atomic reusable ringbuffer +/// +/// This ringbuffer implementation is designed to be stored in a `static`, +/// therefore all methods take `&self` and not `&mut self`. +/// +/// It is "reusable": when created it has no backing buffer, you can give it +/// one with `init` and take it back with `deinit`, and init it again in the +/// future if needed. This is very non-idiomatic, but helps a lot when storing +/// it in a `static`. +/// +/// One concurrent writer and one concurrent reader are supported, even at +/// different execution priorities (like main and irq). +pub struct RingBuffer { + buf: AtomicPtr, + len: AtomicUsize, + start: AtomicUsize, + end: AtomicUsize, +} + +pub struct Reader<'a>(&'a RingBuffer); +pub struct Writer<'a>(&'a RingBuffer); + +impl RingBuffer { + /// Create a new empty ringbuffer. + pub const fn new() -> Self { + Self { + buf: AtomicPtr::new(core::ptr::null_mut()), + len: AtomicUsize::new(0), + start: AtomicUsize::new(0), + end: AtomicUsize::new(0), + } + } + + /// Initialize the ring buffer with a buffer. + /// + /// # Safety + /// - The buffer (`buf .. buf+len`) must be valid memory until `deinit` is called. + /// - Must not be called concurrently with any other methods. + pub unsafe fn init(&self, buf: *mut u8, len: usize) { + // Ordering: it's OK to use `Relaxed` because this is not called + // concurrently with other methods. + self.buf.store(buf, Ordering::Relaxed); + self.len.store(len, Ordering::Relaxed); + self.start.store(0, Ordering::Relaxed); + self.end.store(0, Ordering::Relaxed); + } + + /// Deinitialize the ringbuffer. + /// + /// After calling this, the ringbuffer becomes empty, as if it was + /// just created with `new()`. + /// + /// # Safety + /// - Must not be called concurrently with any other methods. + pub unsafe fn deinit(&self) { + // Ordering: it's OK to use `Relaxed` because this is not called + // concurrently with other methods. + self.len.store(0, Ordering::Relaxed); + self.start.store(0, Ordering::Relaxed); + self.end.store(0, Ordering::Relaxed); + } + + /// Create a reader. + /// + /// # Safety + /// + /// Only one reader can exist at a time. + pub unsafe fn reader(&self) -> Reader<'_> { + Reader(self) + } + + /// Create a writer. + /// + /// # Safety + /// + /// Only one writer can exist at a time. + pub unsafe fn writer(&self) -> Writer<'_> { + Writer(self) + } + + pub fn is_full(&self) -> bool { + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + + self.wrap(end + 1) == start + } + + pub fn is_empty(&self) -> bool { + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + + start == end + } + + fn wrap(&self, n: usize) -> usize { + let len = self.len.load(Ordering::Relaxed); + + assert!(n <= len); + if n == len { + 0 + } else { + n + } + } +} + +impl<'a> Writer<'a> { + /// Push data into the buffer in-place. + /// + /// The closure `f` is called with a free part of the buffer, it must write + /// some data to it and return the amount of bytes written. + pub fn push(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize { + let (p, n) = self.push_buf(); + let buf = unsafe { slice::from_raw_parts_mut(p, n) }; + let n = f(buf); + self.push_done(n); + n + } + + /// Push one data byte. + /// + /// Returns true if pushed succesfully. + pub fn push_one(&mut self, val: u8) -> bool { + let n = self.push(|f| match f { + [] => 0, + [x, ..] => { + *x = val; + 1 + } + }); + n != 0 + } + + /// Get a buffer where data can be pushed to. + /// + /// Write data to the start of the buffer, then call `push_done` with + /// however many bytes you've pushed. + /// + /// The buffer is suitable to DMA to. + /// + /// If the ringbuf is full, size=0 will be returned. + /// + /// The buffer stays valid as long as no other `Writer` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn push_buf(&mut self) -> (*mut u8, usize) { + // Ordering: popping writes `start` last, so we read `start` first. + // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. + let start = self.0.start.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let end = self.0.end.load(Ordering::Relaxed); + + let n = if start <= end { + len - end - (start == 0) as usize + } else { + start - end - 1 + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n); + (unsafe { buf.add(end) }, n) + } + + pub fn push_done(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + let end = self.0.end.load(Ordering::Relaxed); + + // Ordering: write `end` last, with Release ordering. + // The ordering ensures no preceding memory accesses (such as writing + // the actual data in the buffer) can be reordered down past it, which + // will guarantee the reader sees them after reading from `end`. + self.0.end.store(self.0.wrap(end + n), Ordering::Release); + } +} + +impl<'a> Reader<'a> { + /// Pop data from the buffer in-place. + /// + /// The closure `f` is called with the next data, it must process + /// some data from it and return the amount of bytes processed. + pub fn pop(&mut self, f: impl FnOnce(&[u8]) -> usize) -> usize { + let (p, n) = self.pop_buf(); + let buf = unsafe { slice::from_raw_parts(p, n) }; + let n = f(buf); + self.pop_done(n); + n + } + + /// Pop one data byte. + /// + /// Returns true if popped succesfully. + pub fn pop_one(&mut self) -> Option { + let mut res = None; + self.pop(|f| match f { + &[] => 0, + &[x, ..] => { + res = Some(x); + 1 + } + }); + res + } + + /// Get a buffer where data can be popped from. + /// + /// Read data from the start of the buffer, then call `pop_done` with + /// however many bytes you've processed. + /// + /// The buffer is suitable to DMA from. + /// + /// If the ringbuf is empty, size=0 will be returned. + /// + /// The buffer stays valid as long as no other `Reader` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn pop_buf(&mut self) -> (*mut u8, usize) { + // Ordering: pushing writes `end` last, so we read `end` first. + // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. + // This is needed to guarantee we "see" the data written by the writer. + let end = self.0.end.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let start = self.0.start.load(Ordering::Relaxed); + + let n = if end < start { len - start } else { end - start }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n); + (unsafe { buf.add(start) }, n) + } + + pub fn pop_done(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + + let start = self.0.start.load(Ordering::Relaxed); + + // Ordering: write `start` last, with Release ordering. + // The ordering ensures no preceding memory accesses (such as reading + // the actual data) can be reordered down past it. This is necessary + // because writing to `start` is effectively freeing the read part of the + // buffer, which "gives permission" to the writer to write to it again. + // Therefore, all buffer accesses must be completed before this. + self.0.start.store(self.0.wrap(start + n), Ordering::Release); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn push_pop() { + let mut b = [0; 4]; + let rb = RingBuffer::new(); + unsafe { + rb.init(b.as_mut_ptr(), 4); + + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + // If capacity is 4, we can fill it up to 3. + assert_eq!(3, buf.len()); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + 3 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + + rb.writer().push(|buf| { + // If it's full, we can push 0 bytes. + assert_eq!(0, buf.len()); + 0 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + assert_eq!(1, buf[0]); + 1 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + 0 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(2, buf[0]); + assert_eq!(3, buf[1]); + 2 + }); + + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + + rb.writer().push(|buf| { + assert_eq!(1, buf.len()); + buf[0] = 10; + 1 + }); + + rb.writer().push(|buf| { + assert_eq!(2, buf.len()); + buf[0] = 11; + buf[1] = 12; + 2 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + } + } +} diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 5d2649d0..b2a35cd3 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -4,6 +4,7 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub mod atomic_ring_buffer; pub mod drop; mod macros; mod peripheral; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 770d8e25..daa60f9c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -13,7 +13,7 @@ flavors = [ ] [features] -defmt = ["dep:defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/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. diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index fa466c8a..32029f81 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,337 +1,421 @@ -use core::future::poll_fn; -use core::task::{Poll, Waker}; +use core::future::{poll_fn, Future}; +use core::slice; +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 embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_sync::waitqueue::AtomicWaker; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); -impl<'d, T: Instance> State<'d, T> { +pub struct State { + tx_waker: AtomicWaker, + tx_buf: RingBuffer, + rx_waker: AtomicWaker, + rx_buf: RingBuffer, +} + +impl State { pub const fn new() -> Self { - Self(StateStorage::new()) + Self { + rx_buf: RingBuffer::new(), + tx_buf: RingBuffer::new(), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } } } -pub struct RxState<'d, T: Instance>(StateStorage>); -impl<'d, T: Instance> RxState<'d, T> { - pub const fn new() -> Self { - Self(StateStorage::new()) - } -} - -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, FullStateInner<'d, T>>, + phantom: PhantomData<&'d mut T>, } pub struct BufferedUartRx<'d, T: Instance> { - inner: PeripheralMutex<'d, RxStateInner<'d, T>>, + phantom: PhantomData<&'d mut T>, } pub struct BufferedUartTx<'d, T: Instance> { - inner: PeripheralMutex<'d, TxStateInner<'d, T>>, + phantom: PhantomData<&'d mut T>, } -impl<'d, T: Instance> Unpin for BufferedUart<'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( - state: &'d mut State<'d, T>, - _uart: Uart<'d, T, M>, + pub fn new( + _uart: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], - ) -> BufferedUart<'d, T> { - into_ref!(irq); + config: Config, + ) -> Self { + into_ref!(tx, rx); + Self::new_inner( + irq, + tx.map_into(), + rx.map_into(), + None, + None, + tx_buffer, + rx_buffer, + config, + ) + } + + pub fn new_with_rtscts( + _uart: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(tx, rx, cts, rts); + Self::new_inner( + irq, + tx.map_into(), + rx.map_into(), + Some(rts.map_into()), + Some(cts.map_into()), + tx_buffer, + rx_buffer, + config, + ) + } + + fn new_inner( + irq: impl Peripheral

+ 'd, + mut tx: PeripheralRef<'d, AnyPin>, + mut rx: PeripheralRef<'d, AnyPin>, + mut rts: Option>, + mut cts: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(irq); + super::Uart::<'d, T, Async>::init( + Some(tx.reborrow()), + Some(rx.reborrow()), + rts.as_mut().map(|x| x.reborrow()), + cts.as_mut().map(|x| x.reborrow()), + config, + ); + + let state = T::state(); + let regs = T::regs(); + + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - let r = T::regs(); unsafe { - r.uartimsc().modify(|w| { + regs.uartimsc().modify(|w| { w.set_rxim(true); w.set_rtim(true); w.set_txim(true); }); } - Self { - 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), - }, - }), - } + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); + + Self { phantom: PhantomData } } } impl<'d, T: Instance> BufferedUartRx<'d, T> { - pub fn new( - state: &'d mut RxState<'d, T>, - _uart: UartRx<'d, T, M>, + pub fn new( + _uart: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, rx_buffer: &'d mut [u8], - ) -> BufferedUartRx<'d, T> { - into_ref!(irq); + config: Config, + ) -> Self { + into_ref!(rx); + Self::new_inner(irq, rx.map_into(), None, rx_buffer, config) + } + + pub fn new_with_rts( + _uart: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(rx, rts); + Self::new_inner(irq, rx.map_into(), Some(rts.map_into()), rx_buffer, config) + } + + fn new_inner( + irq: impl Peripheral

+ 'd, + mut rx: PeripheralRef<'d, AnyPin>, + mut rts: Option>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(irq); + super::Uart::<'d, T, Async>::init( + None, + Some(rx.reborrow()), + rts.as_mut().map(|x| x.reborrow()), + None, + config, + ); + + let state = T::state(); + let regs = T::regs(); + + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - let r = T::regs(); unsafe { - r.uartimsc().modify(|w| { + regs.uartimsc().modify(|w| { w.set_rxim(true); w.set_rtim(true); }); } - Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { - phantom: PhantomData, + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); - buf: RingBuffer::new(rx_buffer), - waker: WakerRegistration::new(), - }), - } + Self { phantom: PhantomData } + } + + fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { + poll_fn(move |cx| { + let state = T::state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let n = rx_reader.pop(|data| { + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + n + }); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(n)) + }) + } + + fn fill_buf<'a>() -> impl Future> { + poll_fn(move |cx| { + let state = T::state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let (p, n) = rx_reader.pop_buf(); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + + let buf = unsafe { slice::from_raw_parts(p, n) }; + Poll::Ready(Ok(buf)) + }) + } + + fn consume(amt: usize) { + let state = T::state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + rx_reader.pop_done(amt) } } impl<'d, T: Instance> BufferedUartTx<'d, T> { - pub fn new( - state: &'d mut TxState<'d, T>, - _uart: UartTx<'d, T, M>, + pub fn new( + _uart: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], - ) -> BufferedUartTx<'d, T> { - into_ref!(irq); + config: Config, + ) -> Self { + into_ref!(tx); + Self::new_inner(irq, tx.map_into(), None, tx_buffer, config) + } + + pub fn new_with_cts( + _uart: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(tx, cts); + Self::new_inner(irq, tx.map_into(), Some(cts.map_into()), tx_buffer, config) + } + + fn new_inner( + irq: impl Peripheral

+ 'd, + mut tx: PeripheralRef<'d, AnyPin>, + mut cts: Option>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(irq); + super::Uart::<'d, T, Async>::init( + Some(tx.reborrow()), + None, + None, + cts.as_mut().map(|x| x.reborrow()), + config, + ); + + let state = T::state(); + let regs = T::regs(); + + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let r = T::regs(); unsafe { - r.uartimsc().modify(|w| { + regs.uartimsc().modify(|w| { w.set_txim(true); }); } - Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { - phantom: PhantomData, + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); - buf: RingBuffer::new(tx_buffer), - waker: WakerRegistration::new(), - }), - } - } -} - -impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.rx.on_interrupt(); - self.tx.on_interrupt(); - } -} - -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) + Self { phantom: PhantomData } } - 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, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - let r = T::regs(); - unsafe { - let ris = r.uartris().read(); - // Clear interrupt flags - r.uarticr().modify(|w| { - w.set_rxic(true); - w.set_rtic(true); + fn write<'a>(buf: &'a [u8]) -> impl Future> + 'a { + poll_fn(move |cx| { + let state = T::state(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let n = tx_writer.push(|data| { + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + n }); - - 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(); - self.buf.push(1); - } else { - warn!("RX buffer full, discard received byte"); - } - - if self.buf.is_full() { - self.waker.wake(); - } - } - - if ris.rtris() { - self.waker.wake(); - }; - } - } -} - -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, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - let r = T::regs(); - unsafe { - 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(); + if n == 0 { + state.tx_waker.register(cx.waker()); + return Poll::Pending; } else { - // Disable interrupt until we have something to transmit again - r.uartimsc().modify(|w| { - w.set_txim(false); - }); + unsafe { T::Interrupt::steal() }.pend(); } + + Poll::Ready(Ok(n)) + }) + } + + fn flush() -> impl Future> { + poll_fn(move |cx| { + let state = T::state(); + if !state.tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + } +} + +impl<'d, T: Instance> Drop for BufferedUart<'d, T> { + fn drop(&mut self) { + unsafe { + T::Interrupt::steal().disable(); + let state = T::state(); + state.tx_buf.deinit(); + state.rx_buf.deinit(); + } + } +} + +impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { + fn drop(&mut self) { + unsafe { + T::Interrupt::steal().disable(); + let state = T::state(); + state.tx_buf.deinit(); + state.rx_buf.deinit(); + } + } +} + +impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { + fn drop(&mut self) { + unsafe { + T::Interrupt::steal().disable(); + let state = T::state(); + state.tx_buf.deinit(); + state.rx_buf.deinit(); + } + } +} + +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + trace!("on_interrupt"); + + let r = T::regs(); + let s = T::state(); + + unsafe { + // RX + + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.peris() { + warn!("Parity error"); + r.uarticr().write(|w| { + w.set_peic(true); + }); + } + if ris.feris() { + warn!("Framing error"); + r.uarticr().write(|w| { + w.set_feic(true); + }); + } + if ris.beris() { + warn!("Break error"); + r.uarticr().write(|w| { + w.set_beic(true); + }); + } + if ris.oeris() { + warn!("Overrun error"); + r.uarticr().write(|w| { + w.set_oeic(true); + }); + } + + let mut rx_writer = s.rx_buf.writer(); + if !r.uartfr().read().rxfe() { + let val = r.uartdr().read().data(); + if !rx_writer.push_one(val) { + warn!("RX buffer full, discard received byte"); + } + s.rx_waker.wake(); + } + + // TX + let mut tx_reader = s.tx_buf.reader(); + if let Some(val) = tx_reader.pop_one() { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + r.uartdr().write(|w| w.set_data(val)); + s.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); } } } @@ -356,108 +440,52 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - poll_fn(move |cx| { - let (res, do_pend) = self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.rx.read(buf, cx.waker()) - }); - - if do_pend { - self.inner.pend(); - } - - res - }) - .await + BufferedUartRx::<'d, T>::read(buf).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - poll_fn(move |cx| { - let (res, do_pend) = self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.read(buf, cx.waker()) - }); - - if do_pend { - self.inner.pend(); - } - - res - }) - .await + Self::read(buf).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.rx.fill_buf(cx.waker()) - }) - }) - .await + BufferedUartRx::<'d, T>::fill_buf().await } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| state.rx.consume(amt)); - if signal { - self.inner.pend(); - } + BufferedUartRx::<'d, T>::consume(amt) } } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.fill_buf(cx.waker()) - }) - }) - .await + Self::fill_buf().await } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| state.consume(amt)); - if signal { - self.inner.pend(); - } + Self::consume(amt) } } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); - if empty { - self.inner.pend(); - } - poll - }) - .await + BufferedUartTx::<'d, T>::write(buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))).await + BufferedUartTx::<'d, T>::flush().await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); - if empty { - self.inner.pend(); - } - poll - }) - .await + Self::write(buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))).await + Self::flush().await } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 56c25e18..7e7bcaf3 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -7,6 +7,11 @@ use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::{BufferedUart, BufferedUartRx, BufferedUartTx}; + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DataBits { DataBits5, @@ -196,7 +201,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(tx, rx); - Self::new_inner(uart, rx.map_into(), tx.map_into(), None, None, None, None, config) + Self::new_inner(uart, tx.map_into(), rx.map_into(), None, None, None, None, config) } /// Create a new UART with hardware flow control (RTS/CTS) @@ -211,8 +216,8 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { into_ref!(tx, rx, cts, rts); Self::new_inner( uart, - rx.map_into(), tx.map_into(), + rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), None, @@ -235,8 +240,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> { into_ref!(tx, rx, tx_dma, rx_dma); Self::new_inner( uart, - rx.map_into(), tx.map_into(), + rx.map_into(), None, None, Some(tx_dma.map_into()), @@ -259,8 +264,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> { into_ref!(tx, rx, cts, rts, tx_dma, rx_dma); Self::new_inner( uart, - rx.map_into(), tx.map_into(), + rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), Some(tx_dma.map_into()), @@ -273,41 +278,52 @@ impl<'d, T: Instance> Uart<'d, T, Async> { impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { fn new_inner( _uart: impl Peripheral

+ 'd, - tx: PeripheralRef<'d, AnyPin>, - rx: PeripheralRef<'d, AnyPin>, - rts: Option>, - cts: Option>, + mut tx: PeripheralRef<'d, AnyPin>, + mut rx: PeripheralRef<'d, AnyPin>, + mut rts: Option>, + mut cts: Option>, tx_dma: Option>, rx_dma: Option>, config: Config, ) -> Self { - into_ref!(_uart); + Self::init( + Some(tx.reborrow()), + Some(rx.reborrow()), + rts.as_mut().map(|x| x.reborrow()), + cts.as_mut().map(|x| x.reborrow()), + config, + ); + Self { + tx: UartTx::new(tx_dma), + rx: UartRx::new(rx_dma), + } + } + + fn init( + tx: Option>, + rx: Option>, + rts: Option>, + cts: Option>, + config: Config, + ) { + let r = T::regs(); unsafe { - let r = T::regs(); - - tx.io().ctrl().write(|w| w.set_funcsel(2)); - rx.io().ctrl().write(|w| w.set_funcsel(2)); - - tx.pad_ctrl().write(|w| { - w.set_ie(true); - }); - - rx.pad_ctrl().write(|w| { - w.set_ie(true); - }); - + if let Some(pin) = &tx { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } + if let Some(pin) = &rx { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } if let Some(pin) = &cts { pin.io().ctrl().write(|w| w.set_funcsel(2)); - pin.pad_ctrl().write(|w| { - w.set_ie(true); - }); + pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { pin.io().ctrl().write(|w| w.set_funcsel(2)); - pin.pad_ctrl().write(|w| { - w.set_ie(true); - }); + pin.pad_ctrl().write(|w| w.set_ie(true)); } let clk_base = crate::clocks::clk_peri_freq(); @@ -359,11 +375,6 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_rtsen(rts.is_some()); }); } - - Self { - tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), - } } } @@ -611,11 +622,6 @@ mod eha { } } -#[cfg(feature = "nightly")] -mod buffered; -#[cfg(feature = "nightly")] -pub use buffered::*; - mod sealed { use super::*; @@ -628,6 +634,9 @@ mod sealed { type Interrupt: crate::interrupt::Interrupt; fn regs() -> pac::uart::Uart; + + #[cfg(feature = "nightly")] + fn state() -> &'static buffered::State; } pub trait TxPin {} pub trait RxPin {} @@ -663,6 +672,12 @@ macro_rules! impl_instance { fn regs() -> pac::uart::Uart { pac::$inst } + + #[cfg(feature = "nightly")] + fn state() -> &'static buffered::State { + static STATE: buffered::State = buffered::State::new(); + &STATE + } } impl Instance for peripherals::$inst {} }; diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 9cc20bb9..bea9283e 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -5,7 +5,7 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_rp::interrupt; -use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embassy_rp::uart::{BufferedUart, Config}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -17,25 +17,22 @@ async fn main(_spawner: Spawner) { 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; 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); + let mut uart = BufferedUart::new(uart, irq, tx, rx, tx_buf, rx_buf, config); // 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, + 1u8, 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, ]; uart.write_all(&data).await.unwrap(); info!("Done writing"); - let mut buf = [0; 32]; + let mut buf = [0; 31]; uart.read_exact(&mut buf).await.unwrap(); assert_eq!(buf, data); From 6b8ab32536bf2e831ec424b5aaf489bb1f53d017 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 26 Nov 2022 15:22:31 +0100 Subject: [PATCH 173/374] Use &mut self for start methods --- embassy-nrf/src/i2s.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index d5815160..bc90dbc9 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -645,7 +645,8 @@ pub struct OutputStream<'d, T: Instance> { impl<'d, T: Instance> OutputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: &[S]) -> Result<(), Error> + #[allow(unused_mut)] + pub async fn start(&mut self, buffer: &[S]) -> Result<(), Error> where S: Sample, { @@ -694,7 +695,8 @@ pub struct InputStream<'d, T: Instance> { impl<'d, T: Instance> InputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: &mut [S]) -> Result<(), Error> + #[allow(unused_mut)] + pub async fn start(&mut self, buffer: &mut [S]) -> Result<(), Error> where S: Sample, { @@ -743,7 +745,8 @@ pub struct FullDuplexStream<'d, T: Instance> { impl<'d, T: Instance> FullDuplexStream<'d, T> { /// Prepare the initial buffers and start the I2S transfer. - pub async fn start(&self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> + #[allow(unused_mut)] + pub async fn start(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> where S: Sample, { From d438d1b685acb41b29d01c64bc422836760cb3de Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 16:24:20 -0500 Subject: [PATCH 174/374] sync: Fix nightly feature compilation after upgrade to embedded-io 0.4.0 --- embassy-sync/src/lib.rs | 3 +- embassy-sync/src/pipe.rs | 74 ++++++++++------------------------------ 2 files changed, 20 insertions(+), 57 deletions(-) diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 80bb907a..f9435ecf 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index cd577f34..905686ac 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -352,8 +352,6 @@ where mod io_impls { use core::convert::Infallible; - use futures_util::FutureExt; - use super::*; impl embedded_io::Io for Pipe { @@ -361,30 +359,18 @@ mod io_impls { } impl embedded_io::asynch::Read for Pipe { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Pipe::read(self, buf).map(Ok) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(Pipe::read(self, buf).await) } } impl embedded_io::asynch::Write for Pipe { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Pipe::write(self, buf).map(Ok) + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(Pipe::write(self, buf).await) } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - futures_util::future::ready(Ok(())) + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } @@ -393,30 +379,18 @@ mod io_impls { } impl embedded_io::asynch::Read for &Pipe { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Pipe::read(self, buf).map(Ok) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(Pipe::read(self, buf).await) } } impl embedded_io::asynch::Write for &Pipe { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Pipe::write(self, buf).map(Ok) + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(Pipe::write(self, buf).await) } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - futures_util::future::ready(Ok(())) + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } @@ -425,12 +399,8 @@ mod io_impls { } impl embedded_io::asynch::Read for Reader<'_, M, N> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Reader::read(self, buf).map(Ok) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(Reader::read(self, buf).await) } } @@ -439,20 +409,12 @@ mod io_impls { } impl embedded_io::asynch::Write for Writer<'_, M, N> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Writer::write(self, buf).map(Ok) + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(Writer::write(self, buf).await) } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - futures_util::future::ready(Ok(())) + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } } From 3ca14ba4e9feee2f0d34c8dd1c0e426d2090d8d8 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 16:26:58 -0500 Subject: [PATCH 175/374] usb-driver: Remove unncessary lifetime --- embassy-rp/src/usb.rs | 2 +- embassy-stm32/src/usb/usb.rs | 2 +- embassy-usb-driver/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 32fc2632..dfc2e9da 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -638,7 +638,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 64 } - async fn setup<'a>(&'a mut self) -> [u8; 8] { + async fn setup(&mut self) -> [u8; 8] { loop { trace!("SETUP read waiting"); let regs = T::regs(); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 0ba06cce..460abfe2 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -799,7 +799,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { usize::from(self.max_packet_size) } - async fn setup<'a>(&'a mut self) -> [u8; 8] { + async fn setup(&mut self) -> [u8; 8] { loop { trace!("SETUP read waiting"); poll_fn(|cx| { diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 85e9267d..0a76ba6f 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -220,7 +220,7 @@ pub trait ControlPipe { fn max_packet_size(&self) -> usize; /// Reads a single setup packet from the endpoint. - async fn setup<'a>(&'a mut self) -> [u8; 8]; + async fn setup(&mut self) -> [u8; 8]; /// Reads a DATA OUT packet into `buf` in response to a control write request. /// From 4d84b5469ece6e7ad1597b6da41972a0ea391672 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 16:32:18 -0500 Subject: [PATCH 176/374] Drive-by documentation link fixes --- embassy-rp/src/rtc/mod.rs | 2 +- embassy-usb-driver/src/lib.rs | 4 ++-- embassy-usb/src/class/hid.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index e4b6f0b1..c173909c 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -164,7 +164,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { } } -/// Errors that can occur on methods on [RtcClock] +/// Errors that can occur on methods on [RealTimeClock] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { /// An invalid DateTime was given or stored on the hardware. diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 0a76ba6f..9300ff81 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -184,7 +184,7 @@ pub trait Bus { /// /// # Errors /// - /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support + /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// simulating a disconnect or it has not been enabled at creation time. fn force_reset(&mut self) -> Result<(), Unsupported> { Err(Unsupported) @@ -194,7 +194,7 @@ pub trait Bus { /// /// # Errors /// - /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support + /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// remote wakeup or it has not been enabled at creation time. async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 4d1fa995..b967aba0 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -299,7 +299,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> { /// **Note:** If `N` > the maximum packet size of the endpoint (i.e. output /// reports may be split across multiple packets) and this method's future /// is dropped after some packets have been read, the next call to `read()` - /// will return a [`ReadError::SyncError()`]. The range in the sync error + /// will return a [`ReadError::Sync`]. The range in the sync error /// indicates the portion `buf` that was filled by the current call to /// `read()`. If the dropped future used the same `buf`, then `buf` will /// contain the full report. From aedcc472c9fa133f73fcf3a6139d178c81159452 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 17:59:01 -0500 Subject: [PATCH 177/374] time: Fix nighly feature compilation after upgrade to embedded-hal-async 0.2.0-alpha.0 --- embassy-time/src/delay.rs | 16 ++++------------ embassy-time/src/lib.rs | 3 ++- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index ff6b6869..0ca176ab 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -33,26 +33,18 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - - use futures_util::FutureExt; - use super::*; use crate::Timer; impl embedded_hal_async::delay::DelayUs for Delay { type Error = core::convert::Infallible; - type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { - Timer::after(Duration::from_micros(micros as _)).map(Ok) + async fn delay_us(&mut self, micros: u32) -> Result<(), Self::Error> { + Ok(Timer::after(Duration::from_micros(micros as _)).await) } - type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { - Timer::after(Duration::from_millis(millis as _)).map(Ok) + async fn delay_ms(&mut self, millis: u32) -> Result<(), Self::Error> { + Ok(Timer::after(Duration::from_millis(millis as _)).await) } } } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 586aa28d..8b0aebe1 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] From 787e5d49078de4b09919711e6d9870a7c2a86b32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Nov 2022 00:12:13 +0100 Subject: [PATCH 178/374] Add -time, -sync to CI with all features. --- ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index cd1c0786..a99a5f32 100755 --- a/ci.sh +++ b/ci.sh @@ -36,6 +36,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \ From 2a35a0944420ecf3d33e9684b9bb5756500cb851 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 6 Sep 2022 17:44:20 +0200 Subject: [PATCH 179/374] stm32: Fix H7 unaligned erase --- embassy-stm32/src/flash/h7.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 3f2129de..3178b3be 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -79,24 +79,19 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { let from = from - super::FLASH_BASE as u32; let to = to - super::FLASH_BASE as u32; - let bank_size = (super::FLASH_SIZE / 2) as u32; - - let (bank, start, end) = if to <= bank_size { + let (start, end) = if to <= super::FLASH_SIZE as u32 { let start_sector = from / super::ERASE_SIZE as u32; let end_sector = to / super::ERASE_SIZE as u32; - (0, start_sector, end_sector) - } else if from >= SECOND_BANK_OFFSET as u32 && to <= (SECOND_BANK_OFFSET as u32 + bank_size) { - let start_sector = (from - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32; - let end_sector = (to - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32; - (1, start_sector, end_sector) + (start_sector, end_sector) } else { - error!("Attempting to write outside of defined sectors"); + error!("Attempting to write outside of defined sectors {:x} {:x}", from, to); return Err(Error::Unaligned); }; - trace!("Erasing bank {}, sectors from {} to {}", bank, start, end); + trace!("Erasing sectors from {} to {}", start, end); for sector in start..end { - let ret = erase_sector(pac::FLASH.bank(bank), sector as u8); + let bank = if sector >= 8 { 1 } else { 0 }; + let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); if ret.is_err() { return ret; } From 4cc046312359d0a930eb7c6c78df25c1fc1ea6e6 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 23 Nov 2022 10:11:19 +0100 Subject: [PATCH 180/374] stm32: Add basic support for DMA priority settings --- embassy-stm32/src/dma/bdma.rs | 7 +++++-- embassy-stm32/src/dma/dma.rs | 7 +++++-- embassy-stm32/src/dma/mod.rs | 8 +++++--- embassy-stm32/src/lib.rs | 17 ++++++++++++++++- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 674255dd..e6ce05b7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -3,6 +3,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; +use embassy_cortex_m::interrupt::Priority; use embassy_sync::waitqueue::AtomicWaker; use super::{TransferOptions, Word, WordSize}; @@ -38,10 +39,12 @@ impl State { static STATE: State = State::new(); /// safety: must be called only once -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { - crate::interrupt::$irq::steal().enable(); + let irq = crate::interrupt::$irq::steal(); + irq.set_priority(irq_priority); + irq.enable(); }; } crate::_generated::init_bdma(); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index a45b8780..97a3df08 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,6 +1,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; +use embassy_cortex_m::interrupt::Priority; use embassy_sync::waitqueue::AtomicWaker; use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize}; @@ -67,10 +68,12 @@ impl State { static STATE: State = State::new(); /// safety: must be called only once -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { - interrupt::$irq::steal().enable(); + let irq = interrupt::$irq::steal(); + irq.set_priority(irq_priority); + irq.enable(); }; } crate::_generated::init_dma(); diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index cc030a93..74bce6aa 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -12,6 +12,8 @@ use core::mem; use core::pin::Pin; use core::task::{Context, Poll, Waker}; +#[cfg(any(dma, bdma))] +use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{impl_peripheral, into_ref}; #[cfg(dmamux)] @@ -294,11 +296,11 @@ pub struct NoDma; impl_peripheral!(NoDma); // safety: must be called only once at startup -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) { #[cfg(bdma)] - bdma::init(); + bdma::init(bdma_priority); #[cfg(dma)] - dma::init(); + dma::init(dma_priority); #[cfg(dmamux)] dmamux::init(); #[cfg(gpdma)] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index d7443eac..16c46ca2 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,6 +79,8 @@ pub(crate) mod _generated { // Reexports pub use _generated::{peripherals, Peripherals}; pub use embassy_cortex_m::executor; +#[cfg(any(dma, bdma))] +use embassy_cortex_m::interrupt::Priority; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] @@ -91,6 +93,10 @@ pub struct Config { pub rcc: rcc::Config, #[cfg(dbgmcu)] pub enable_debug_during_sleep: bool, + #[cfg(bdma)] + pub bdma_interrupt_priority: Priority, + #[cfg(dma)] + pub dma_interrupt_priority: Priority, } impl Default for Config { @@ -99,6 +105,10 @@ impl Default for Config { rcc: Default::default(), #[cfg(dbgmcu)] enable_debug_during_sleep: true, + #[cfg(bdma)] + bdma_interrupt_priority: Priority::P0, + #[cfg(dma)] + dma_interrupt_priority: Priority::P0, } } } @@ -137,7 +147,12 @@ pub fn init(config: Config) -> Peripherals { } gpio::init(); - dma::init(); + dma::init( + #[cfg(bdma)] + config.bdma_interrupt_priority, + #[cfg(dma)] + config.dma_interrupt_priority, + ); #[cfg(feature = "exti")] exti::init(); From 199504be564b231154e07c58bcc52b11afdc9fe7 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:09:47 +0100 Subject: [PATCH 181/374] Optimization to be able to work with only 2 buffers --- embassy-nrf/src/i2s.rs | 48 ++++++++++++++++++++-------- examples/nrf/src/bin/i2s_waveform.rs | 20 ++++++------ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index bc90dbc9..08d4093f 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -398,6 +398,7 @@ impl<'d, T: Instance> I2S<'d, T> { ) -> FullDuplexStream<'d, T> { self.sdout = Some(sdout.into_ref().map_into()); self.sdin = Some(sdin.into_ref().map_into()); + FullDuplexStream { _p: self.build() } } @@ -549,6 +550,16 @@ impl<'d, T: Instance> I2S<'d, T> { let device = Device::::new(); + device.update_tx(buffer_ptr)?; + + Self::wait_tx_ptr_update().await; + + compiler_fence(Ordering::SeqCst); + + Ok(()) + } + + async fn wait_tx_ptr_update() { let drop = OnDrop::new(move || { trace!("TX DROP: Stopping"); @@ -566,6 +577,7 @@ impl<'d, T: Instance> I2S<'d, T> { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); + let device = Device::::new(); if device.is_tx_ptr_updated() { trace!("TX POLL: Ready"); device.reset_tx_ptr_event(); @@ -578,12 +590,7 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.update_tx(buffer_ptr)?; - - compiler_fence(Ordering::SeqCst); drop.defuse(); - - Ok(()) } async fn receive_from_ram(buffer_ptr: *mut [S]) -> Result<(), Error> @@ -599,6 +606,16 @@ impl<'d, T: Instance> I2S<'d, T> { let device = Device::::new(); + device.update_rx(buffer_ptr)?; + + Self::wait_rx_ptr_update().await; + + compiler_fence(Ordering::SeqCst); + + Ok(()) + } + + async fn wait_rx_ptr_update() { let drop = OnDrop::new(move || { trace!("RX DROP: Stopping"); @@ -616,6 +633,7 @@ impl<'d, T: Instance> I2S<'d, T> { poll_fn(|cx| { T::state().rx_waker.register(cx.waker()); + let device = Device::::new(); if device.is_rx_ptr_updated() { trace!("RX POLL: Ready"); device.reset_rx_ptr_event(); @@ -628,13 +646,7 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.update_rx(buffer_ptr)?; - - compiler_fence(Ordering::SeqCst); - drop.defuse(); - - Ok(()) } } @@ -666,6 +678,8 @@ impl<'d, T: Instance> OutputStream<'d, T> { device.start(); + I2S::::wait_tx_ptr_update().await; + Ok(()) } @@ -716,6 +730,8 @@ impl<'d, T: Instance> InputStream<'d, T> { device.start(); + I2S::::wait_rx_ptr_update().await; + Ok(()) } @@ -746,7 +762,7 @@ pub struct FullDuplexStream<'d, T: Instance> { impl<'d, T: Instance> FullDuplexStream<'d, T> { /// Prepare the initial buffers and start the I2S transfer. #[allow(unused_mut)] - pub async fn start(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> + pub async fn start(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> where S: Sample, { @@ -768,6 +784,9 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { device.start(); + I2S::::wait_tx_ptr_update().await; + I2S::::wait_rx_ptr_update().await; + Ok(()) } @@ -782,7 +801,7 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { /// The buffers must not be written/read while being used by the DMA, /// which takes two other `send_and_receive` operations being awaited. #[allow(unused_mut)] - pub async fn send_and_receive_from_ram(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> + pub async fn send_and_receive_from_ram(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> where S: Sample, { @@ -903,7 +922,7 @@ impl Device { #[inline(always)] fn enable_rx_ptr_interrupt(&self) { trace!("RX PTR INTERRUPT: Enabled"); - self.0.intenclr.write(|w| w.rxptrupd().clear()); + self.0.intenset.write(|w| w.rxptrupd().set()); } #[inline(always)] @@ -974,6 +993,7 @@ impl Sample for i32 { } /// A 4-bytes aligned [Buffer]. +#[derive(Clone, Copy)] #[repr(align(4))] pub struct AlignedBuffer([T; N]); diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf/src/bin/i2s_waveform.rs index 81858ff5..13b1300e 100644 --- a/examples/nrf/src/bin/i2s_waveform.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -12,7 +12,8 @@ use {defmt_rtt as _, panic_probe as _}; type Sample = i16; -const NUM_SAMPLES: usize = 6000; +const NUM_BUFFERS: usize = 2; +const NUM_SAMPLES: usize = 50; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -30,31 +31,27 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(I2S); let mut output_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28); - let mut buffers: [i2s::AlignedBuffer; 3] = [ - i2s::AlignedBuffer::default(), - i2s::AlignedBuffer::default(), - i2s::AlignedBuffer::default(), - ]; + let mut buffers: [i2s::AlignedBuffer; NUM_BUFFERS] = + [i2s::AlignedBuffer::default(); NUM_BUFFERS]; let mut waveform = Waveform::new(1.0 / sample_rate as f32); waveform.process(&mut buffers[0]); - waveform.process(&mut buffers[1]); output_stream.start(&buffers[0]).await.expect("I2S Start"); let mut index = 1; loop { + waveform.process(&mut buffers[index]); + if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { error!("{}", err); } index += 1; - if index >= 3 { + if index >= NUM_BUFFERS { index = 0; } - - waveform.process(&mut buffers[index]); } } @@ -67,7 +64,8 @@ struct Waveform { impl Waveform { fn new(inv_sample_rate: f32) -> Self { - let carrier = SineOsc::new(); + let mut carrier = SineOsc::new(); + carrier.set_frequency(110.0, inv_sample_rate); let mut freq_mod = SineOsc::new(); freq_mod.set_frequency(8.0, inv_sample_rate); From 3135ad016d580a2a5912c4ade8a1f61c1d0375d7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 29 Nov 2022 12:05:46 +0100 Subject: [PATCH 182/374] Bump embedded-nal-async to 0.3.0 --- embassy-net/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 0ac53b50..86d4aa10 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -52,7 +52,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" atomic-polyfill = "1.0.1" -embedded-nal-async = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0", optional = true } +embedded-nal-async = { version = "0.3.0", optional = true } [dependencies.smoltcp] version = "0.8.0" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index de945999..f665f7c1 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -20,7 +20,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = { version = "=0.2.0-alpha.0" } -embedded-nal-async = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0" } +embedded-nal-async = "0.3.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } From 1dcb0ea1f5dbd9357c7c05627f06abbcaaedd134 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 29 Nov 2022 21:15:24 +0100 Subject: [PATCH 183/374] Bump defmt-rtt to 0.4 --- embassy-boot/stm32/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 31 files changed, 31 insertions(+), 31 deletions(-) diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 9d12c6cf..2fc169b3 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -15,7 +15,7 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } log = { version = "0.4", optional = true } embassy-sync = { path = "../../embassy-sync" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index a5d82b60..1e7a5a84 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 3a184356..aa279fb7 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 8d9c4490..1ec0643a 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index b4314aa7..a4eefe2a 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index a17d336a..36eada29 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 683f2c86..67efda74 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index b879c0d7..4b2e02dd 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index e3bc0e49..fecbfc51 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index b417a40d..8a6f5364 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 4ddd1c99..be659e02 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 64dbc663..8b95ac3a 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -24,7 +24,7 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" static_cell = "1.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 364f738d..60a8ba94 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -16,7 +16,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a56c546e..8f410048 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 6be131f3..53f369b3 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -13,7 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index f6adda2a..afaf9a0c 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 27188dd1..69ebef78 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -13,7 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 594b6161..62d3f08d 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -12,7 +12,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index caabe068..f4d674cd 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -13,7 +13,7 @@ embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index f5673718..e7273c9f 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index ecda2880..8a57a8ef 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -12,7 +12,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index f665f7c1..11ce3505 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -13,7 +13,7 @@ embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index f5fd18f1..86933a62 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -19,7 +19,7 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" embedded-storage = "0.3.0" embedded-io = "0.4.0" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 9460febf..6e3b2103 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 9092d85c..45d3dd36 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -14,7 +14,7 @@ embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 376e9e51..73ad5078 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -17,7 +17,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 1cf1078d..d88fdda5 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 5b96fa19..e27b4527 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index c827d2b7..690481bb 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -15,7 +15,7 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index a6102b27..a07b479e 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -12,7 +12,7 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightl embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" -defmt-rtt = "0.3.0" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f74a3c70..08a775ea 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -20,7 +20,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } defmt = "0.3.0" -defmt-rtt = "0.3.0" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" From e0ea5dfdb2f32974ea4d278402dbdd42d1530bbd Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Wed, 30 Nov 2022 09:26:16 +0100 Subject: [PATCH 184/374] embassy-stm32: Allow SPI with DMA to implement blocking embbeded-hal traits --- embassy-stm32/src/spi/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 17198fc2..ab4352a5 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -8,7 +8,7 @@ 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 self::sealed::WordSize; -use crate::dma::{slice_ptr_parts, NoDma, Transfer}; +use crate::dma::{slice_ptr_parts, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::AnyPin; use crate::pac::spi::{regs, vals, Spi as Regs}; @@ -812,7 +812,7 @@ mod eh02 { // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 macro_rules! impl_blocking { ($w:ident) => { - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, Tx, Rx> { type Error = Error; fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { @@ -820,7 +820,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, Tx, Rx> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { @@ -849,19 +849,19 @@ mod eh1 { } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, Tx, Rx> { fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.blocking_read(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, Tx, Rx> { fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBus for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } From 71df28e269ae3157b4b07df7f270b2a81c08803e Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Tue, 29 Nov 2022 20:46:04 -0500 Subject: [PATCH 185/374] rp: Add an RngCore impl based on ROSC.RANDOMBIT This has the potential to not be random, but it should not be an issue if default clock settings are used. --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/clocks.rs | 39 ++++++++++++++++++++++++++++++++++++++- embassy-rp/src/lib.rs | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index daa60f9c..284d458c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -55,6 +55,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } +rand_core = "0.6.4" 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 1c446f38..85c9bbb7 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -5,7 +5,7 @@ use crate::{pac, reset}; const XOSC_MHZ: u32 = 12; /// safety: must be called exactly once at bootup -pub unsafe fn init() { +pub(crate) unsafe fn init() { // Reset everything except: // - QSPI (we're using it to run this code!) // - PLLs (it may be suicide if that's what's clocking us) @@ -196,3 +196,40 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: // Turn on post divider p.pwr().modify(|w| w.set_postdivpd(false)); } + +/// Random number generator based on the ROSC RANDOMBIT register. +/// +/// This will not produce random values if the ROSC is stopped or run at some +/// harmonic of the bus frequency. With default clock settings these are not +/// issues. +pub struct RoscRng; + +impl RoscRng { + fn next_u8() -> u8 { + let random_reg = pac::ROSC.randombit(); + let mut acc = 0; + for _ in 0..u8::BITS { + acc <<= 1; + acc |= unsafe { random_reg.read().randombit() as u8 }; + } + acc + } +} + +impl rand_core::RngCore for RoscRng { + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + Ok(self.fill_bytes(dest)) + } + + fn next_u32(&mut self) -> u32 { + rand_core::impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + dest.fill_with(Self::next_u8) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index e5b07c90..d21b5f7b 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -21,7 +21,7 @@ pub mod uart; #[cfg(feature = "nightly")] pub mod usb; -mod clocks; +pub mod clocks; pub mod flash; mod reset; From e1d7d8d841c59015d4600a66c65b7a8efffc62fc Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 30 Nov 2022 14:11:32 +0100 Subject: [PATCH 186/374] stm32: Enable fifo for buffered uart --- embassy-stm32/src/usart/buffered.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index acd96d7c..d024bedc 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -112,6 +112,9 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { unsafe { r.cr1().modify(|w| { + #[cfg(lpuart_v2)] + w.set_fifoen(true); + w.set_rxneie(true); w.set_idleie(true); }); From bb89a2341cca1aad79bc6d5f3532008541c9e428 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 1 Dec 2022 18:26:22 +0100 Subject: [PATCH 187/374] feat: embassy-boot for rp2040 Add embassy-boot support for RP2040, with examples for the Raspberry Pi Pico. Co-authored-by: Mathias Koch --- ci.sh | 3 + docs/modules/ROOT/pages/bootloader.adoc | 1 + embassy-boot/rp/Cargo.toml | 71 ++++++ embassy-boot/rp/README.md | 26 ++ embassy-boot/rp/build.rs | 8 + embassy-boot/rp/src/fmt.rs | 225 ++++++++++++++++++ embassy-boot/rp/src/lib.rs | 90 +++++++ embassy-rp/src/flash.rs | 5 + .../boot/application/rp/.cargo/config.toml | 12 + examples/boot/application/rp/Cargo.toml | 33 +++ examples/boot/application/rp/README.md | 28 +++ examples/boot/application/rp/build.rs | 35 +++ examples/boot/application/rp/memory.x | 15 ++ examples/boot/application/rp/src/bin/a.rs | 52 ++++ examples/boot/application/rp/src/bin/b.rs | 23 ++ .../boot/bootloader/rp/.cargo/config.toml | 8 + examples/boot/bootloader/rp/Cargo.toml | 29 +++ examples/boot/bootloader/rp/README.md | 17 ++ examples/boot/bootloader/rp/build.rs | 28 +++ examples/boot/bootloader/rp/memory.x | 19 ++ examples/boot/bootloader/rp/src/main.rs | 51 ++++ 21 files changed, 779 insertions(+) create mode 100644 embassy-boot/rp/Cargo.toml create mode 100644 embassy-boot/rp/README.md create mode 100644 embassy-boot/rp/build.rs create mode 100644 embassy-boot/rp/src/fmt.rs create mode 100644 embassy-boot/rp/src/lib.rs create mode 100644 examples/boot/application/rp/.cargo/config.toml create mode 100644 examples/boot/application/rp/Cargo.toml create mode 100644 examples/boot/application/rp/README.md create mode 100644 examples/boot/application/rp/build.rs create mode 100644 examples/boot/application/rp/memory.x create mode 100644 examples/boot/application/rp/src/bin/a.rs create mode 100644 examples/boot/application/rp/src/bin/b.rs create mode 100644 examples/boot/bootloader/rp/.cargo/config.toml create mode 100644 examples/boot/bootloader/rp/Cargo.toml create mode 100644 examples/boot/bootloader/rp/README.md create mode 100644 examples/boot/bootloader/rp/build.rs create mode 100644 examples/boot/bootloader/rp/memory.x create mode 100644 examples/boot/bootloader/rp/src/main.rs diff --git a/ci.sh b/ci.sh index a99a5f32..9f79c87b 100755 --- a/ci.sh +++ b/ci.sh @@ -81,6 +81,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ @@ -106,6 +107,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/nrf --bin b \ + --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/rp --bin b \ --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f3 --bin b \ --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f7 --bin b \ --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32h7 --bin b \ @@ -114,6 +116,7 @@ cargo batch \ --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32l4 --bin b \ --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wl --bin b \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/bluepill-stm32f103c8 \ diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index 7dbfeb3e..b50de5ab 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -15,6 +15,7 @@ The bootloader supports * nRF52 with and without softdevice * STM32 L4, WB, WL, L1, L0, F3, F7 and H7 +* Raspberry Pi: RP2040 In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work. diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml new file mode 100644 index 00000000..93099b23 --- /dev/null +++ b/embassy-boot/rp/Cargo.toml @@ -0,0 +1,71 @@ +[package] +edition = "2021" +name = "embassy-boot-rp" +version = "0.1.0" +description = "Bootloader lib for RP2040 chips" +license = "MIT OR Apache-2.0" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/rp/src/" +target = "thumbv6m-none-eabi" + +[lib] + +[dependencies] +defmt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } +log = { version = "0.4", optional = true } + +embassy-sync = { path = "../../embassy-sync" } +embassy-rp = { path = "../../embassy-rp", default-features = false, features = ["nightly"] } +embassy-boot = { path = "../boot", default-features = false } +cortex-m = { version = "0.7.6" } +cortex-m-rt = { version = "0.7" } +embedded-storage = "0.3.0" +embedded-storage-async = "0.3.0" +cfg-if = "1.0.0" + +[features] +defmt = [ + "dep:defmt", + "embassy-boot/defmt", + "embassy-rp/defmt", +] +log = [ + "dep:log", + "embassy-boot/log", + "embassy-rp/log", +] +debug = ["defmt-rtt"] + +[profile.dev] +debug = 2 +debug-assertions = true +incremental = false +opt-level = 'z' +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 'z' +overflow-checks = false + +# do not optimize proc-macro crates = faster builds from scratch +[profile.dev.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[profile.release.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false diff --git a/embassy-boot/rp/README.md b/embassy-boot/rp/README.md new file mode 100644 index 00000000..c0c2d85f --- /dev/null +++ b/embassy-boot/rp/README.md @@ -0,0 +1,26 @@ +# embassy-boot-rp + +An [Embassy](https://embassy.dev) project. + +An adaptation of `embassy-boot` for RP2040. + +NOTE: The applications using this bootloader should not link with the `link-rp.x` linker script. + +## Features + +* Configure bootloader partitions based on linker script. +* Load applications from active partition. + +## Minimum supported Rust version (MSRV) + +`embassy-boot-rp` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/rp/build.rs b/embassy-boot/rp/build.rs new file mode 100644 index 00000000..2cbc7ef5 --- /dev/null +++ b/embassy-boot/rp/build.rs @@ -0,0 +1,8 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + if target.starts_with("thumbv6m-") { + println!("cargo:rustc-cfg=armv6m"); + } +} diff --git a/embassy-boot/rp/src/fmt.rs b/embassy-boot/rp/src/fmt.rs new file mode 100644 index 00000000..06697081 --- /dev/null +++ b/embassy-boot/rp/src/fmt.rs @@ -0,0 +1,225 @@ +#![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-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs new file mode 100644 index 00000000..85fc8182 --- /dev/null +++ b/embassy-boot/rp/src/lib.rs @@ -0,0 +1,90 @@ +#![no_std] +#![feature(type_alias_impl_trait)] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] +mod fmt; + +pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; +use embassy_rp::flash::{ERASE_SIZE, WRITE_SIZE}; + +/// A bootloader for RP2040 devices. +pub struct BootLoader { + boot: embassy_boot::BootLoader, + magic: AlignedBuffer, + page: AlignedBuffer, +} + +impl BootLoader { + /// Create a new bootloader instance using the supplied partitions for active, dfu and state. + pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + Self { + boot: embassy_boot::BootLoader::new(active, dfu, state), + magic: AlignedBuffer([0; WRITE_SIZE]), + page: AlignedBuffer([0; ERASE_SIZE]), + } + } + + /// Inspect the bootloader state and perform actions required before booting, such as swapping + /// firmware. + pub fn prepare(&mut self, flash: &mut F) -> usize { + match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { + Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), + Err(_) => panic!("boot prepare error!"), + } + } + + /// Boots the application. + /// + /// # Safety + /// + /// This modifies the stack pointer and reset vector and will run code placed in the active partition. + pub unsafe fn load(&mut self, start: usize) -> ! { + trace!("Loading app at 0x{:x}", start); + #[allow(unused_mut)] + let mut p = cortex_m::Peripherals::steal(); + #[cfg(not(armv6m))] + p.SCB.invalidate_icache(); + p.SCB.vtor.write(start as u32); + + cortex_m::asm::bootload(start as *const u32) + } +} + +impl Default for BootLoader { + /// Create a new bootloader instance using parameters from linker script + fn default() -> Self { + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_active_start: u32; + static __bootloader_active_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let active = unsafe { + Partition::new( + &__bootloader_active_start as *const u32 as usize, + &__bootloader_active_end as *const u32 as usize, + ) + }; + let dfu = unsafe { + Partition::new( + &__bootloader_dfu_start as *const u32 as usize, + &__bootloader_dfu_end as *const u32 as usize, + ) + }; + let state = unsafe { + Partition::new( + &__bootloader_state_start as *const u32 as usize, + &__bootloader_state_end as *const u32 as usize, + ) + }; + + trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); + trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); + trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); + + Self::new(active, dfu, state) + } +} diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index d09cc62f..a972d5f6 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -59,6 +59,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + trace!( + "Reading from 0x{:x} to 0x{:x}", + FLASH_BASE + offset as usize, + FLASH_BASE + offset as usize + bytes.len() + ); check_read(self, offset, bytes.len())?; let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) }; diff --git a/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml new file mode 100644 index 00000000..edbd0a86 --- /dev/null +++ b/examples/boot/application/rp/.cargo/config.toml @@ -0,0 +1,12 @@ +[unstable] +build-std = ["core"] +build-std-features = ["panic_immediate_abort"] + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip RP2040" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml new file mode 100644 index 00000000..8d826790 --- /dev/null +++ b/examples/boot/application/rp/Cargo.toml @@ -0,0 +1,33 @@ +[package] +edition = "2021" +name = "embassy-boot-rp-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } +embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } +embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" } +embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } + +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"], optional = true } +panic-reset = { version = "0.1.1", optional = true } +embedded-hal = { version = "0.2.6" } + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" + +[features] +default = ["panic-reset"] +debug = [ + "embassy-rp/defmt", + "embassy-boot-rp/defmt", + "panic-probe" +] + +[profile.release] +debug = true diff --git a/examples/boot/application/rp/README.md b/examples/boot/application/rp/README.md new file mode 100644 index 00000000..41304c52 --- /dev/null +++ b/examples/boot/application/rp/README.md @@ -0,0 +1,28 @@ +# Examples using bootloader + +Example for Raspberry Pi Pico demonstrating the bootloader. The example consists of application binaries, 'a' +which waits for 5 seconds before flashing the 'b' binary, which blinks the LED. + +NOTE: The 'b' binary does not mark the new binary as active, so if you reset the device, it will roll back to the 'a' binary before automatically updating it again. + +## Prerequisites + +* `cargo-binutils` +* `cargo-flash` +* `embassy-boot-rp` + +## Usage + +``` +# Flash bootloader +cargo flash --manifest-path ../../bootloader/rp/Cargo.toml --release --chip RP2040 + +# Build 'b' +cargo build --release --bin b + +# Generate binary for 'b' +cargo objcopy --release --bin b -- -O binary b.bin + +# Flash `a` (which includes b.bin) +cargo flash --release --bin a --chip RP2040 +``` diff --git a/examples/boot/application/rp/build.rs b/examples/boot/application/rp/build.rs new file mode 100644 index 00000000..30691aa9 --- /dev/null +++ b/examples/boot/application/rp/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/boot/application/rp/memory.x b/examples/boot/application/rp/memory.x new file mode 100644 index 00000000..c1947311 --- /dev/null +++ b/examples/boot/application/rp/memory.x @@ -0,0 +1,15 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K + FLASH : ORIGIN = 0x10007000, LENGTH = 512K + DFU : ORIGIN = 0x10087000, LENGTH = 516K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2); + +__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs new file mode 100644 index 00000000..3736c914 --- /dev/null +++ b/examples/boot/application/rp/src/bin/a.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt_rtt as _; +use embassy_boot_rp::*; +use embassy_executor::Spawner; +use embassy_rp::flash::Flash; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Timer}; +#[cfg(feature = "panic-probe")] +use panic_probe as _; +#[cfg(feature = "panic-reset")] +use panic_reset as _; + +static APP_B: &[u8] = include_bytes!("../../b.bin"); +const FLASH_SIZE: usize = 2 * 1024 * 1024; + +#[embassy_executor::main] +async fn main(_s: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut led = Output::new(p.PIN_25, Level::Low); + + let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + + let mut updater = FirmwareUpdater::default(); + + Timer::after(Duration::from_secs(5)).await; + led.set_high(); + let mut offset = 0; + let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); + defmt::info!("preparing update"); + let mut writer = updater + .prepare_update_blocking(&mut flash) + .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) + .unwrap(); + defmt::info!("writer created, starting write"); + for chunk in APP_B.chunks(4096) { + buf.0[..chunk.len()].copy_from_slice(chunk); + defmt::info!("writing block at offset {}", offset); + writer + .write_block_blocking(offset, &buf.0[..], &mut flash, 256) + .unwrap(); + offset += chunk.len(); + } + defmt::info!("firmware written, marking update"); + updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); + Timer::after(Duration::from_secs(2)).await; + led.set_low(); + defmt::info!("update marked, resetting"); + cortex_m::peripheral::SCB::sys_reset(); +} diff --git a/examples/boot/application/rp/src/bin/b.rs b/examples/boot/application/rp/src/bin/b.rs new file mode 100644 index 00000000..47dec329 --- /dev/null +++ b/examples/boot/application/rp/src/bin/b.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_rp::gpio; +use embassy_time::{Duration, Timer}; +use gpio::{Level, Output}; +use {defmt_rtt as _, panic_reset as _}; + +#[embassy_executor::main] +async fn main(_s: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(100)).await; + + led.set_low(); + Timer::after(Duration::from_millis(100)).await; + } +} diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml new file mode 100644 index 00000000..18bd4dfe --- /dev/null +++ b/examples/boot/bootloader/rp/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip RP2040" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml new file mode 100644 index 00000000..580ced22 --- /dev/null +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -0,0 +1,29 @@ +[package] +edition = "2021" +name = "rp-bootloader-example" +version = "0.1.0" +description = "Example bootloader for RP2040 chips" +license = "MIT OR Apache-2.0" + +[dependencies] +defmt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } + +embassy-rp = { path = "../../../../embassy-rp", default-features = false, features = ["nightly"] } +embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = { version = "0.7" } +embedded-storage = "0.3.0" +embedded-storage-async = "0.3.0" +cfg-if = "1.0.0" + +[features] +defmt = [ + "dep:defmt", + "embassy-boot-rp/defmt", + "embassy-rp/defmt", +] +debug = ["defmt-rtt", "defmt"] + +[profile.release] +debug = true diff --git a/examples/boot/bootloader/rp/README.md b/examples/boot/bootloader/rp/README.md new file mode 100644 index 00000000..064e8727 --- /dev/null +++ b/examples/boot/bootloader/rp/README.md @@ -0,0 +1,17 @@ +# Bootloader for RP2040 + +The bootloader uses `embassy-boot` to interact with the flash. + +# Usage + +Flashing the bootloader + +``` +cargo flash --release --chip RP2040 +``` + +To debug, use `cargo run` and enable the debug feature flag + +``` rust +cargo run --release --features debug +``` diff --git a/examples/boot/bootloader/rp/build.rs b/examples/boot/bootloader/rp/build.rs new file mode 100644 index 00000000..c201704a --- /dev/null +++ b/examples/boot/bootloader/rp/build.rs @@ -0,0 +1,28 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); + if env::var("CARGO_FEATURE_DEFMT").is_ok() { + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + } +} diff --git a/examples/boot/bootloader/rp/memory.x b/examples/boot/bootloader/rp/memory.x new file mode 100644 index 00000000..d6ef3846 --- /dev/null +++ b/examples/boot/bootloader/rp/memory.x @@ -0,0 +1,19 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 24K + BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K + ACTIVE : ORIGIN = 0x10007000, LENGTH = 512K + DFU : ORIGIN = 0x10087000, LENGTH = 516K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2); + +__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(BOOT2); +__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(BOOT2); + +__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs new file mode 100644 index 00000000..5028ec68 --- /dev/null +++ b/examples/boot/bootloader/rp/src/main.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::{entry, exception}; +#[cfg(feature = "defmt")] +use defmt_rtt as _; +use embassy_boot_rp::*; +use embassy_rp::flash::{Flash, ERASE_SIZE}; +use embassy_rp::peripherals::FLASH; + +const FLASH_SIZE: usize = 2 * 1024 * 1024; + +#[entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + + // Uncomment this if you are debugging the bootloader with debugger/RTT attached, + // as it prevents a hard fault when accessing flash 'too early' after boot. + /* + for i in 0..10000000 { + cortex_m::asm::nop(); + } + */ + + let mut bl: BootLoader = BootLoader::default(); + let flash: Flash<'_, FLASH, FLASH_SIZE> = Flash::new(p.FLASH); + let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); + core::mem::drop(flash); + + unsafe { bl.load(start) } +} + +#[no_mangle] +#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +unsafe extern "C" fn HardFault() { + cortex_m::peripheral::SCB::sys_reset(); +} + +#[exception] +unsafe fn DefaultHandler(_: i16) -> ! { + const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; + let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + + panic!("DefaultHandler #{:?}", irqn); +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + cortex_m::asm::udf(); +} From 02abe00439ba873945bd6b60546a200b3da751f1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 3 Dec 2022 00:56:16 +0100 Subject: [PATCH 188/374] net: don't use UnsafeCell. The "must not be called reentrantly" invariant is too "global" to maintain comfortably, and the cost of the RefCell is negligible, so this was a case of premature optimization. --- embassy-net/src/stack.rs | 32 ++++++++++------------ embassy-net/src/tcp.rs | 59 ++++++++++++++++++---------------------- embassy-net/src/udp.rs | 41 ++++++++++++---------------- 3 files changed, 57 insertions(+), 75 deletions(-) diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 3a761075..63108740 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; @@ -62,8 +62,8 @@ pub enum ConfigStrategy { } pub struct Stack { - pub(crate) socket: UnsafeCell, - inner: UnsafeCell>, + pub(crate) socket: RefCell, + inner: RefCell>, } struct Inner { @@ -81,8 +81,6 @@ pub(crate) struct SocketStack { next_local_port: u16, } -unsafe impl Send for Stack {} - impl Stack { pub fn new( device: D, @@ -143,40 +141,38 @@ impl Stack { } Self { - socket: UnsafeCell::new(socket), - inner: UnsafeCell::new(inner), + socket: RefCell::new(socket), + inner: RefCell::new(inner), } } - /// SAFETY: must not call reentrantly. - unsafe fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { - f(&*self.socket.get(), &*self.inner.get()) + fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { + f(&*self.socket.borrow(), &*self.inner.borrow()) } - /// SAFETY: must not call reentrantly. - unsafe fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { - f(&mut *self.socket.get(), &mut *self.inner.get()) + fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { + f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) } pub fn ethernet_address(&self) -> [u8; 6] { - unsafe { self.with(|_s, i| i.device.device.ethernet_address()) } + self.with(|_s, i| i.device.device.ethernet_address()) } pub fn is_link_up(&self) -> bool { - unsafe { self.with(|_s, i| i.link_up) } + self.with(|_s, i| i.link_up) } pub fn is_config_up(&self) -> bool { - unsafe { self.with(|_s, i| i.config.is_some()) } + self.with(|_s, i| i.config.is_some()) } pub fn config(&self) -> Option { - unsafe { self.with(|_s, i| i.config.clone()) } + self.with(|_s, i| i.config.clone()) } pub async fn run(&self) -> ! { poll_fn(|cx| { - unsafe { self.with_mut(|s, i| i.poll(cx, s)) } + self.with_mut(|s, i| i.poll(cx, s)); Poll::<()>::Pending }) .await; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 85d9e5ee..60386535 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; @@ -68,8 +68,7 @@ impl<'a> TcpWriter<'a> { impl<'a> TcpSocket<'a> { pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { - // safety: not accessed reentrantly. - let s = unsafe { &mut *stack.socket.get() }; + let s = &mut *stack.socket.borrow_mut(); let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; let handle = s.sockets.add(tcp::Socket::new( @@ -93,17 +92,15 @@ impl<'a> TcpSocket<'a> { where T: Into, { - // safety: not accessed reentrantly. - let local_port = unsafe { &mut *self.io.stack.get() }.get_local_port(); + let local_port = self.io.stack.borrow_mut().get_local_port(); - // safety: not accessed reentrantly. - match unsafe { self.io.with_mut(|s, i| s.connect(i, remote_endpoint, local_port)) } { + match { self.io.with_mut(|s, i| s.connect(i, remote_endpoint, local_port)) } { Ok(()) => {} Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState), Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), } - poll_fn(|cx| unsafe { + poll_fn(|cx| { self.io.with_mut(|s, _| match s.state() { tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)), tcp::State::Listen => unreachable!(), @@ -121,14 +118,13 @@ impl<'a> TcpSocket<'a> { where T: Into, { - // safety: not accessed reentrantly. - match unsafe { self.io.with_mut(|s, _| s.listen(local_endpoint)) } { + match self.io.with_mut(|s, _| s.listen(local_endpoint)) { Ok(()) => {} Err(tcp::ListenError::InvalidState) => return Err(AcceptError::InvalidState), Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), } - poll_fn(|cx| unsafe { + poll_fn(|cx| { self.io.with_mut(|s, _| match s.state() { tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { s.register_send_waker(cx.waker()); @@ -149,51 +145,49 @@ impl<'a> TcpSocket<'a> { } pub fn set_timeout(&mut self, duration: Option) { - unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) } + self.io.with_mut(|s, _| s.set_timeout(duration)) } pub fn set_keep_alive(&mut self, interval: Option) { - unsafe { self.io.with_mut(|s, _| s.set_keep_alive(interval)) } + self.io.with_mut(|s, _| s.set_keep_alive(interval)) } pub fn set_hop_limit(&mut self, hop_limit: Option) { - unsafe { self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } + self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } pub fn local_endpoint(&self) -> Option { - unsafe { self.io.with(|s, _| s.local_endpoint()) } + self.io.with(|s, _| s.local_endpoint()) } pub fn remote_endpoint(&self) -> Option { - unsafe { self.io.with(|s, _| s.remote_endpoint()) } + self.io.with(|s, _| s.remote_endpoint()) } pub fn state(&self) -> tcp::State { - unsafe { self.io.with(|s, _| s.state()) } + self.io.with(|s, _| s.state()) } pub fn close(&mut self) { - unsafe { self.io.with_mut(|s, _| s.close()) } + self.io.with_mut(|s, _| s.close()) } pub fn abort(&mut self) { - unsafe { self.io.with_mut(|s, _| s.abort()) } + self.io.with_mut(|s, _| s.abort()) } pub fn may_send(&self) -> bool { - unsafe { self.io.with(|s, _| s.may_send()) } + self.io.with(|s, _| s.may_send()) } pub fn may_recv(&self) -> bool { - unsafe { self.io.with(|s, _| s.may_recv()) } + self.io.with(|s, _| s.may_recv()) } } impl<'a> Drop for TcpSocket<'a> { fn drop(&mut self) { - // safety: not accessed reentrantly. - let s = unsafe { &mut *self.io.stack.get() }; - s.sockets.remove(self.io.handle); + self.io.stack.borrow_mut().sockets.remove(self.io.handle); } } @@ -201,21 +195,19 @@ impl<'a> Drop for TcpSocket<'a> { #[derive(Copy, Clone)] struct TcpIo<'a> { - stack: &'a UnsafeCell, + stack: &'a RefCell, handle: SocketHandle, } impl<'d> TcpIo<'d> { - /// SAFETY: must not call reentrantly. - unsafe fn with(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.get(); + fn with(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R { + let s = &*self.stack.borrow(); let socket = s.sockets.get::(self.handle); f(socket, &s.iface) } - /// SAFETY: must not call reentrantly. - unsafe fn with_mut(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.get(); + fn with_mut(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); let socket = s.sockets.get_mut::(self.handle); let res = f(socket, &mut s.iface); s.waker.wake(); @@ -223,7 +215,7 @@ impl<'d> TcpIo<'d> { } async fn read(&mut self, buf: &mut [u8]) -> Result { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { // CAUTION: smoltcp semantics around EOF are different to what you'd expect // from posix-like IO, so we have to tweak things here. self.with_mut(|s, _| match s.recv_slice(buf) { @@ -244,7 +236,7 @@ impl<'d> TcpIo<'d> { } async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { self.with_mut(|s, _| match s.send_slice(buf) { // Not ready to send (no space in the tx buffer) Ok(0) => { @@ -332,6 +324,7 @@ mod embedded_io_impls { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { + use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index f2e33493..4ddad77d 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; @@ -27,7 +27,7 @@ pub enum Error { } pub struct UdpSocket<'a> { - stack: &'a UnsafeCell, + stack: &'a RefCell, handle: SocketHandle, } @@ -39,8 +39,7 @@ impl<'a> UdpSocket<'a> { tx_meta: &'a mut [PacketMetadata], tx_buffer: &'a mut [u8], ) -> Self { - // safety: not accessed reentrantly. - let s = unsafe { &mut *stack.socket.get() }; + let s = &mut *stack.socket.borrow_mut(); let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; @@ -63,30 +62,26 @@ impl<'a> UdpSocket<'a> { { let mut endpoint = endpoint.into(); - // safety: not accessed reentrantly. if endpoint.port == 0 { // If user didn't specify port allocate a dynamic port. - endpoint.port = unsafe { &mut *self.stack.get() }.get_local_port(); + endpoint.port = self.stack.borrow_mut().get_local_port(); } - // safety: not accessed reentrantly. - match unsafe { self.with_mut(|s, _| s.bind(endpoint)) } { + match self.with_mut(|s, _| s.bind(endpoint)) { Ok(()) => Ok(()), Err(udp::BindError::InvalidState) => Err(BindError::InvalidState), Err(udp::BindError::Unaddressable) => Err(BindError::NoRoute), } } - /// SAFETY: must not call reentrantly. - unsafe fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.get(); + fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { + let s = &*self.stack.borrow(); let socket = s.sockets.get::(self.handle); f(socket, &s.iface) } - /// SAFETY: must not call reentrantly. - unsafe fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.get(); + fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); let socket = s.sockets.get_mut::(self.handle); let res = f(socket, &mut s.iface); s.waker.wake(); @@ -94,7 +89,7 @@ impl<'a> UdpSocket<'a> { } pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { self.with_mut(|s, _| match s.recv_slice(buf) { Ok(x) => Poll::Ready(Ok(x)), // No data ready @@ -113,7 +108,7 @@ impl<'a> UdpSocket<'a> { T: Into, { let remote_endpoint = remote_endpoint.into(); - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) { // Entire datagram has been sent Ok(()) => Poll::Ready(Ok(())), @@ -128,30 +123,28 @@ impl<'a> UdpSocket<'a> { } pub fn endpoint(&self) -> IpListenEndpoint { - unsafe { self.with(|s, _| s.endpoint()) } + self.with(|s, _| s.endpoint()) } pub fn is_open(&self) -> bool { - unsafe { self.with(|s, _| s.is_open()) } + self.with(|s, _| s.is_open()) } pub fn close(&mut self) { - unsafe { self.with_mut(|s, _| s.close()) } + self.with_mut(|s, _| s.close()) } pub fn may_send(&self) -> bool { - unsafe { self.with(|s, _| s.can_send()) } + self.with(|s, _| s.can_send()) } pub fn may_recv(&self) -> bool { - unsafe { self.with(|s, _| s.can_recv()) } + self.with(|s, _| s.can_recv()) } } impl Drop for UdpSocket<'_> { fn drop(&mut self) { - // safety: not accessed reentrantly. - let s = unsafe { &mut *self.stack.get() }; - s.sockets.remove(self.handle); + self.stack.borrow_mut().sockets.remove(self.handle); } } From a2712caab15a673b455184b6f56a7a8d7cdb9290 Mon Sep 17 00:00:00 2001 From: miathedev Date: Sun, 4 Dec 2022 09:38:57 +0100 Subject: [PATCH 189/374] Add random example --- examples/stm32wl/src/bin/random.rs | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/stm32wl/src/bin/random.rs diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs new file mode 100644 index 00000000..182c607f --- /dev/null +++ b/examples/stm32wl/src/bin/random.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::pac; +use embassy_stm32::rng::Rng; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + config.rcc.enable_lsi = true; //Needed for RNG to work + + let p = embassy_stm32::init(config); + unsafe { + pac::RCC.ccipr().modify(|w| { + w.set_rngsel(0b01); + }); + } + + info!("Hello World!"); + + let mut rng = Rng::new(p.RNG); + + let mut buf = [0u8; 16]; + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); + + loop {} +} From ef2b83cc032cd39841f0219144b95259dc5cac79 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Tue, 6 Dec 2022 16:36:07 +0200 Subject: [PATCH 190/374] Reset sdmmc clock on card init --- embassy-stm32/src/sdmmc/mod.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index c91f3c8b..0f8483d5 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -18,6 +18,9 @@ use crate::rcc::RccPeripheral; use crate::time::Hertz; use crate::{peripherals, Peripheral}; +/// Frequency used for SD Card initialization. Must be no higher than 400 kHz. +const SD_INIT_FREQ: Hertz = Hertz(400_000); + /// The signalling scheme used on the SDMMC bus #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -295,7 +298,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { T::reset(); let inner = T::inner(); - let clock = unsafe { inner.new_inner(T::frequency()) }; + unsafe { inner.new_inner() }; irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -314,7 +317,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { d3, config, - clock, + clock: SD_INIT_FREQ, signalling: Default::default(), card: None, } @@ -415,7 +418,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { T::reset(); let inner = T::inner(); - let clock = unsafe { inner.new_inner(T::frequency()) }; + unsafe { inner.new_inner() }; irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -434,7 +437,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { d3, config, - clock, + clock: SD_INIT_FREQ, signalling: Default::default(), card: None, } @@ -561,16 +564,10 @@ impl SdmmcInner { /// # Safety /// /// Access to `regs` registers should be exclusive - unsafe fn new_inner(&self, kernel_clk: Hertz) -> Hertz { + unsafe fn new_inner(&self) { let regs = self.0; - // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be less than 400 kHz. - let (clkdiv, clock) = unwrap!(clk_div(kernel_clk, 400_000)); - regs.clkcr().write(|w| { - w.set_widbus(0); - w.set_clkdiv(clkdiv); w.set_pwrsav(false); w.set_negedge(false); w.set_hwfc_en(true); @@ -582,8 +579,6 @@ impl SdmmcInner { // Power off, writen 00: Clock to the card is stopped; // D[7:0], CMD, and CK are driven high. regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); - - clock } /// Initializes card (if present) and sets the bus at the @@ -605,6 +600,19 @@ impl SdmmcInner { // NOTE(unsafe) We have exclusive access to the peripheral unsafe { + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be less than 400 kHz. + let (clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); + *clock = init_clock; + + // CPSMACT and DPSMACT must be 0 to set WIDBUS + self.wait_idle(); + + regs.clkcr().modify(|w| { + w.set_widbus(0); + w.set_clkdiv(clkdiv); + }); + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); self.cmd(Cmd::idle(), false)?; From 7cbc3aefe6540599e1361a8c3dc51e93e6f83dcd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Dec 2022 19:54:39 +0100 Subject: [PATCH 191/374] rp: implement input for OutputOpenDrain --- embassy-rp/src/gpio.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 71390306..930de206 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -411,6 +411,16 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { pub fn toggle(&mut self) { self.pin.toggle_set_as_output() } + + #[inline] + pub fn is_high(&self) -> bool { + self.pin.is_high() + } + + #[inline] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } } /// GPIO flexible pin. @@ -791,6 +801,18 @@ mod eh02 { } } + impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for OutputOpenDrain<'d, T> { + type Error = Infallible; + + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + fn is_low(&self) -> Result { + Ok(self.is_low()) + } + } + impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d, T> { type Error = Infallible; @@ -946,6 +968,16 @@ mod eh1 { } } + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> { + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + fn is_low(&self) -> Result { + Ok(self.is_low()) + } + } + impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> { type Error = Infallible; } From 54c153673d9e79ea36f687fbdfa3e6f56ff62fc8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Dec 2022 21:09:27 +0100 Subject: [PATCH 192/374] rp: add OutputOpenDrain input test. --- tests/rp/src/bin/gpio.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index af22fe27..80e92d0f 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -78,6 +78,7 @@ async fn main(_spawner: Spawner) { a.set_as_input(); // When an OutputOpenDrain is high, it doesn't drive the pin. + b.set_high(); a.set_pull(Pull::Up); delay(); assert!(a.is_high()); @@ -85,9 +86,8 @@ async fn main(_spawner: Spawner) { delay(); assert!(a.is_low()); - b.set_low(); - // When an OutputOpenDrain is low, it drives the pin low. + b.set_low(); a.set_pull(Pull::Up); delay(); assert!(a.is_low()); @@ -95,14 +95,36 @@ async fn main(_spawner: Spawner) { delay(); assert!(a.is_low()); + // Check high again b.set_high(); - a.set_pull(Pull::Up); delay(); assert!(a.is_high()); a.set_pull(Pull::Down); delay(); assert!(a.is_low()); + + // When an OutputOpenDrain is high, it reads the input value in the pin. + b.set_high(); + a.set_as_input(); + a.set_pull(Pull::Up); + delay(); + assert!(b.is_high()); + a.set_as_output(); + a.set_low(); + delay(); + assert!(b.is_low()); + + // When an OutputOpenDrain is low, it always reads low. + b.set_low(); + a.set_as_input(); + a.set_pull(Pull::Up); + delay(); + assert!(b.is_low()); + a.set_as_output(); + a.set_low(); + delay(); + assert!(b.is_low()); } // FLEX From 7bda01ec240339f8b26d78910c64ad63a2bfc726 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Tue, 6 Dec 2022 23:31:58 +0200 Subject: [PATCH 193/374] Fix comment --- embassy-stm32/src/sdmmc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 0f8483d5..a52c65b9 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -601,7 +601,7 @@ impl SdmmcInner { // NOTE(unsafe) We have exclusive access to the peripheral unsafe { // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be less than 400 kHz. + // the SDMMC_CK frequency must be no more than 400 kHz. let (clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); *clock = init_clock; From f7fe0c1441843b04fa17ba0fe94f8c8d4f851882 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 00:28:38 +0100 Subject: [PATCH 194/374] net: update smoltcp --- embassy-net/Cargo.toml | 2 +- embassy-net/src/device.rs | 14 ++++++-------- embassy-net/src/stack.rs | 21 ++++++--------------- embassy-net/src/tcp.rs | 5 ++++- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 86d4aa10..ac338843 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -57,7 +57,7 @@ embedded-nal-async = { version = "0.3.0", optional = true } [dependencies.smoltcp] version = "0.8.0" git = "https://github.com/smoltcp-rs/smoltcp" -rev = "ed0cf16750a42f30e31fcaf5347915592924b1e3" +rev = "b7a7c4b1c56e8d4c2524c1e3a056c745a13cc09f" default-features = false features = [ "proto-ipv4", diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index c183bd58..4bdfd772 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -12,8 +12,6 @@ pub enum LinkState { Up, } -// 'static required due to the "fake GAT" in smoltcp::phy::Device. -// https://github.com/smoltcp-rs/smoltcp/pull/572 pub trait Device { fn is_transmit_ready(&mut self) -> bool; fn transmit(&mut self, pkt: PacketBuf); @@ -25,7 +23,7 @@ pub trait Device { fn ethernet_address(&self) -> [u8; 6]; } -impl Device for &'static mut T { +impl Device for &mut T { fn is_transmit_ready(&mut self) -> bool { T::is_transmit_ready(self) } @@ -63,11 +61,11 @@ impl DeviceAdapter { } } -impl<'a, D: Device + 'static> SmolDevice<'a> for DeviceAdapter { - type RxToken = RxToken; - type TxToken = TxToken<'a, D>; +impl SmolDevice for DeviceAdapter { + type RxToken<'a> = RxToken where Self: 'a; + type TxToken<'a> = TxToken<'a, D> where Self: 'a; - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let tx_pkt = PacketBox::new(Packet::new())?; let rx_pkt = self.device.receive()?; let rx_token = RxToken { pkt: rx_pkt }; @@ -80,7 +78,7 @@ impl<'a, D: Device + 'static> SmolDevice<'a> for DeviceAdapter { } /// Construct a transmit token. - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { if !self.device.is_transmit_ready() { return None; } diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 63108740..5c4fb044 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -266,21 +266,12 @@ impl Inner { None => {} Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), Some(dhcpv4::Event::Configured(config)) => { - let mut dns_servers = Vec::new(); - for s in &config.dns_servers { - if let Some(addr) = s { - dns_servers.push(addr.clone()).unwrap(); - } - } - - self.apply_config( - s, - Config { - address: config.address, - gateway: config.router, - dns_servers, - }, - ) + let config = Config { + address: config.address, + gateway: config.router, + dns_servers: config.dns_servers, + }; + self.apply_config(s, config) } } } else if old_link_up { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 60386535..73cf2d4e 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -94,7 +94,10 @@ impl<'a> TcpSocket<'a> { { let local_port = self.io.stack.borrow_mut().get_local_port(); - match { self.io.with_mut(|s, i| s.connect(i, remote_endpoint, local_port)) } { + match { + self.io + .with_mut(|s, i| s.connect(i.context(), remote_endpoint, local_port)) + } { Ok(()) => {} Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState), Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), From 5fdd521a767fd8825a2d55d6b833fd99627353d7 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 8 Dec 2022 20:22:50 +0100 Subject: [PATCH 195/374] Move the responsibility to manage buffers to the I2S stream --- embassy-nrf/src/i2s.rs | 157 +++++++++++++++++++-------- examples/nrf/src/bin/i2s_effect.rs | 117 ++++++++++++++++++++ examples/nrf/src/bin/i2s_monitor.rs | 115 ++++++++++++++++++++ examples/nrf/src/bin/i2s_waveform.rs | 26 ++--- 4 files changed, 353 insertions(+), 62 deletions(-) create mode 100644 examples/nrf/src/bin/i2s_effect.rs create mode 100644 examples/nrf/src/bin/i2s_monitor.rs diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 08d4093f..7e950775 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -19,6 +19,8 @@ use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; +pub type DoubleBuffering = MultiBuffering; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -379,27 +381,47 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output only - pub fn output(mut self, sdout: impl Peripheral

+ 'd) -> OutputStream<'d, T> { + pub fn output( + mut self, + sdout: impl Peripheral

+ 'd, + buffers: MultiBuffering, + ) -> OutputStream<'d, T, S, NB, NS> { self.sdout = Some(sdout.into_ref().map_into()); - OutputStream { _p: self.build() } + OutputStream { + _p: self.build(), + buffers, + } } /// I2S input only - pub fn input(mut self, sdin: impl Peripheral

+ 'd) -> InputStream<'d, T> { + pub fn input( + mut self, + sdin: impl Peripheral

+ 'd, + buffers: MultiBuffering, + ) -> InputStream<'d, T, S, NB, NS> { self.sdin = Some(sdin.into_ref().map_into()); - InputStream { _p: self.build() } + InputStream { + _p: self.build(), + buffers, + } } /// I2S full duplex (input and output) - pub fn full_duplex( + pub fn full_duplex( mut self, sdin: impl Peripheral

+ 'd, sdout: impl Peripheral

+ 'd, - ) -> FullDuplexStream<'d, T> { + buffers_out: MultiBuffering, + buffers_in: MultiBuffering, + ) -> FullDuplexStream<'d, T, S, NB, NS> { self.sdout = Some(sdout.into_ref().map_into()); self.sdin = Some(sdin.into_ref().map_into()); - FullDuplexStream { _p: self.build() } + FullDuplexStream { + _p: self.build(), + buffers_out, + buffers_in, + } } fn build(self) -> PeripheralRef<'d, T> { @@ -651,14 +673,19 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output -pub struct OutputStream<'d, T: Instance> { +pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { _p: PeripheralRef<'d, T>, + buffers: MultiBuffering, } -impl<'d, T: Instance> OutputStream<'d, T> { +impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, T, S, NB, NS> { + /// Get a mutable reference to the current buffer. + pub fn buffer(&mut self) -> &mut [S] { + self.buffers.get_mut() + } + /// Prepare the initial buffer and start the I2S transfer. - #[allow(unused_mut)] - pub async fn start(&mut self, buffer: &[S]) -> Result<(), Error> + pub async fn start(&mut self) -> Result<(), Error> where S: Sample, { @@ -672,7 +699,7 @@ impl<'d, T: Instance> OutputStream<'d, T> { device.enable(); device.enable_tx(); - device.update_tx(buffer as *const [S])?; + device.update_tx(self.buffers.switch())?; s.started.store(true, Ordering::Relaxed); @@ -689,28 +716,30 @@ impl<'d, T: Instance> OutputStream<'d, T> { I2S::::stop().await } - /// Sets the given `buffer` for transmission in the DMA. - /// Buffer address must be 4 byte aligned and located in RAM. - /// The buffer must not be written while being used by the DMA, - /// which takes two other `send`s being awaited. - #[allow(unused_mut)] - pub async fn send_from_ram(&mut self, buffer: &[S]) -> Result<(), Error> + /// Sends the current buffer for transmission in the DMA. + /// Switches to use the next available buffer. + pub async fn send(&mut self) -> Result<(), Error> where S: Sample, { - I2S::::send_from_ram(buffer as *const [S]).await + I2S::::send_from_ram(self.buffers.switch()).await } } /// I2S input -pub struct InputStream<'d, T: Instance> { +pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { _p: PeripheralRef<'d, T>, + buffers: MultiBuffering, } -impl<'d, T: Instance> InputStream<'d, T> { +impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<'d, T, S, NB, NS> { + /// Get a mutable reference to the current buffer. + pub fn buffer(&mut self) -> &mut [S] { + self.buffers.get_mut() + } + /// Prepare the initial buffer and start the I2S transfer. - #[allow(unused_mut)] - pub async fn start(&mut self, buffer: &mut [S]) -> Result<(), Error> + pub async fn start(&mut self) -> Result<(), Error> where S: Sample, { @@ -724,7 +753,7 @@ impl<'d, T: Instance> InputStream<'d, T> { device.enable(); device.enable_rx(); - device.update_rx(buffer as *mut [S])?; + device.update_rx(self.buffers.switch())?; s.started.store(true, Ordering::Relaxed); @@ -741,28 +770,32 @@ impl<'d, T: Instance> InputStream<'d, T> { I2S::::stop().await } - /// Sets the given `buffer` for reception from the DMA. - /// Buffer address must be 4 byte aligned and located in RAM. - /// The buffer must not be read while being used by the DMA, - /// which takes two other `receive`s being awaited. + /// Sets the current buffer for reception from the DMA. + /// Switches to use the next available buffer. #[allow(unused_mut)] - pub async fn receive_from_ram(&mut self, buffer: &mut [S]) -> Result<(), Error> + pub async fn receive(&mut self) -> Result<(), Error> where S: Sample, { - I2S::::receive_from_ram(buffer as *mut [S]).await + I2S::::receive_from_ram(self.buffers.switch_mut()).await } } /// I2S full duplex stream (input & output) -pub struct FullDuplexStream<'d, T: Instance> { +pub struct FullDuplexStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { _p: PeripheralRef<'d, T>, + buffers_out: MultiBuffering, + buffers_in: MultiBuffering, } -impl<'d, T: Instance> FullDuplexStream<'d, T> { +impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, T, S, NB, NS> { + /// Get the current output and input buffers. + pub fn buffers(&mut self) -> (&mut [S], &[S]) { + (self.buffers_out.get_mut(), self.buffers_in.get()) + } + /// Prepare the initial buffers and start the I2S transfer. - #[allow(unused_mut)] - pub async fn start(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> + pub async fn start(&mut self) -> Result<(), Error> where S: Sample, { @@ -777,8 +810,8 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { device.enable_tx(); device.enable_rx(); - device.update_tx(buffer_out as *const [S])?; - device.update_rx(buffer_in as *mut [S])?; + device.update_tx(self.buffers_out.switch())?; + device.update_rx(self.buffers_in.switch_mut())?; s.started.store(true, Ordering::Relaxed); @@ -796,17 +829,14 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { I2S::::stop().await } - /// Sets the given `buffer_out` and `buffer_in` for transmission/reception from the DMA. - /// Buffer address must be 4 byte aligned and located in RAM. - /// The buffers must not be written/read while being used by the DMA, - /// which takes two other `send_and_receive` operations being awaited. - #[allow(unused_mut)] - pub async fn send_and_receive_from_ram(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> + /// Sets the current buffers for output and input for transmission/reception from the DMA. + /// Switch to use the next available buffers for output/input. + pub async fn send_and_receive(&mut self) -> Result<(), Error> where S: Sample, { - I2S::::send_from_ram(buffer_out as *const [S]).await?; - I2S::::receive_from_ram(buffer_in as *mut [S]).await?; + I2S::::send_from_ram(self.buffers_out.switch()).await?; + I2S::::receive_from_ram(self.buffers_in.switch_mut()).await?; Ok(()) } } @@ -992,7 +1022,7 @@ impl Sample for i32 { const SCALE: Self = 1 << (Self::WIDTH - 1); } -/// A 4-bytes aligned [Buffer]. +/// A 4-bytes aligned buffer. #[derive(Clone, Copy)] #[repr(align(4))] pub struct AlignedBuffer([T; N]); @@ -1022,6 +1052,43 @@ impl DerefMut for AlignedBuffer { } } +pub struct MultiBuffering { + buffers: [AlignedBuffer; NB], + index: usize, +} + +impl MultiBuffering { + pub fn new() -> Self { + assert!(NB > 1); + Self { + buffers: [AlignedBuffer::::default(); NB], + index: 0, + } + } + + fn get(&self) -> &[S] { + &self.buffers[self.index] + } + + fn get_mut(&mut self) -> &mut [S] { + &mut self.buffers[self.index] + } + + /// Advance to use the next buffer and return a non mutable pointer to the previous one. + fn switch(&mut self) -> *const [S] { + let prev_index = self.index; + self.index = (self.index + 1) % NB; + self.buffers[prev_index].deref() as *const [S] + } + + /// Advance to use the next buffer and return a mutable pointer to the previous one. + fn switch_mut(&mut self) -> *mut [S] { + let prev_index = self.index; + self.index = (self.index + 1) % NB; + self.buffers[prev_index].deref_mut() as *mut [S] + } +} + pub(crate) mod sealed { use core::sync::atomic::AtomicBool; diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf/src/bin/i2s_effect.rs new file mode 100644 index 00000000..3cca005b --- /dev/null +++ b/examples/nrf/src/bin/i2s_effect.rs @@ -0,0 +1,117 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::f32::consts::PI; + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; +use embassy_nrf::interrupt; +use {defmt_rtt as _, panic_probe as _}; + +type Sample = i16; + +const NUM_BUFFERS: usize = 2; +const NUM_SAMPLES: usize = 4; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + + let sample_rate = master_clock.sample_rate(); + info!("Sample rate: {}", sample_rate); + + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); + + let irq = interrupt::take!(I2S); + let buffers_out = MultiBuffering::::new(); + let buffers_in = MultiBuffering::::new(); + let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( + p.P0_29, + p.P0_28, + buffers_out, + buffers_in, + ); + + let mut modulator = SineOsc::new(); + modulator.set_frequency(8.0, 1.0 / sample_rate as f32); + modulator.set_amplitude(1.0); + + full_duplex_stream.start().await.expect("I2S Start"); + + loop { + let (buff_out, buff_in) = full_duplex_stream.buffers(); + for i in 0..NUM_SAMPLES { + let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample; + buff_out[i] = buff_in[i] * modulation; + } + + if let Err(err) = full_duplex_stream.send_and_receive().await { + error!("{}", err); + } + } +} + +struct SineOsc { + amplitude: f32, + modulo: f32, + phase_inc: f32, +} + +impl SineOsc { + const B: f32 = 4.0 / PI; + const C: f32 = -4.0 / (PI * PI); + const P: f32 = 0.225; + + pub fn new() -> Self { + Self { + amplitude: 1.0, + modulo: 0.0, + phase_inc: 0.0, + } + } + + pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { + self.phase_inc = freq * inv_sample_rate; + } + + pub fn set_amplitude(&mut self, amplitude: f32) { + self.amplitude = amplitude; + } + + pub fn generate(&mut self) -> f32 { + let signal = self.parabolic_sin(self.modulo); + self.modulo += self.phase_inc; + if self.modulo < 0.0 { + self.modulo += 1.0; + } else if self.modulo > 1.0 { + self.modulo -= 1.0; + } + signal * self.amplitude + } + + fn parabolic_sin(&mut self, modulo: f32) -> f32 { + let angle = PI - modulo * 2.0 * PI; + let y = Self::B * angle + Self::C * angle * abs(angle); + Self::P * (y * abs(y) - y) + y + } +} + +#[inline] +fn abs(value: f32) -> f32 { + if value < 0.0 { + -value + } else { + value + } +} + +#[inline] +fn bipolar_to_unipolar(value: f32) -> f32 { + (value + 1.0) / 2.0 +} diff --git a/examples/nrf/src/bin/i2s_monitor.rs b/examples/nrf/src/bin/i2s_monitor.rs new file mode 100644 index 00000000..48eb7d58 --- /dev/null +++ b/examples/nrf/src/bin/i2s_monitor.rs @@ -0,0 +1,115 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{debug, error, info}; +use embassy_executor::Spawner; +use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; +use embassy_nrf::interrupt; +use embassy_nrf::pwm::{Prescaler, SimplePwm}; +use {defmt_rtt as _, panic_probe as _}; + +type Sample = i16; + +const NUM_SAMPLES: usize = 500; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + + let sample_rate = master_clock.sample_rate(); + info!("Sample rate: {}", sample_rate); + + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); + + let irq = interrupt::take!(I2S); + let buffers = DoubleBuffering::::new(); + let mut input_stream = + I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); + + // Configure the PWM to use the pins corresponding to the RGB leds + let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); + pwm.set_prescaler(Prescaler::Div1); + pwm.set_max_duty(255); + + let mut rms_online = RmsOnline::::default(); + + input_stream.start().await.expect("I2S Start"); + + loop { + let rms = rms_online.process(input_stream.buffer()); + let rgb = rgb_from_rms(rms); + + debug!("RMS: {}, RGB: {:?}", rms, rgb); + for i in 0..3 { + pwm.set_duty(i, rgb[i].into()); + } + + if let Err(err) = input_stream.receive().await { + error!("{}", err); + } + } +} + +/// RMS from 0.0 until 0.75 will give green with a proportional intensity +/// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity +/// RMS above 0.9 will give a red with a proportional intensity +fn rgb_from_rms(rms: f32) -> [u8; 3] { + if rms < 0.75 { + let intensity = rms / 0.75; + [0, (intensity * 165.0) as u8, 0] + } else if rms < 0.9 { + let intensity = (rms - 0.75) / 0.15; + [200, 165 - (165.0 * intensity) as u8, 0] + } else { + let intensity = (rms - 0.9) / 0.1; + [200 + (55.0 * intensity) as u8, 0, 0] + } +} + +pub struct RmsOnline { + pub squares: [f32; N], + pub head: usize, +} + +impl Default for RmsOnline { + fn default() -> Self { + RmsOnline { + squares: [0.0; N], + head: 0, + } + } +} + +impl RmsOnline { + pub fn reset(&mut self) { + self.squares = [0.0; N]; + self.head = 0; + } + + pub fn process(&mut self, buf: &[Sample]) -> f32 { + buf.iter() + .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32)); + + let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v); + Self::approx_sqrt(sum_of_squares / N as f32) + } + + pub fn push(&mut self, signal: f32) { + let square = signal * signal; + self.squares[self.head] = square; + self.head = (self.head + 1) % N; + } + + /// Approximated sqrt taken from [micromath] + /// + /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17 + /// + fn approx_sqrt(value: f32) -> f32 { + f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1) + } +} diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf/src/bin/i2s_waveform.rs index 13b1300e..1b0e8ebc 100644 --- a/examples/nrf/src/bin/i2s_waveform.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -6,13 +6,12 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Channels, Config, MasterClock, Sample as _, SampleWidth, I2S}; +use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; use embassy_nrf::interrupt; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; -const NUM_BUFFERS: usize = 2; const NUM_SAMPLES: usize = 50; #[embassy_executor::main] @@ -29,29 +28,22 @@ async fn main(_spawner: Spawner) { .channels(Channels::MonoLeft); let irq = interrupt::take!(I2S); - let mut output_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28); - - let mut buffers: [i2s::AlignedBuffer; NUM_BUFFERS] = - [i2s::AlignedBuffer::default(); NUM_BUFFERS]; + let buffers = DoubleBuffering::::new(); + let mut output_stream = + I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); let mut waveform = Waveform::new(1.0 / sample_rate as f32); - waveform.process(&mut buffers[0]); + waveform.process(output_stream.buffer()); - output_stream.start(&buffers[0]).await.expect("I2S Start"); + output_stream.start().await.expect("I2S Start"); - let mut index = 1; loop { - waveform.process(&mut buffers[index]); + waveform.process(output_stream.buffer()); - if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { + if let Err(err) = output_stream.send().await { error!("{}", err); } - - index += 1; - if index >= NUM_BUFFERS { - index = 0; - } } } @@ -68,7 +60,7 @@ impl Waveform { carrier.set_frequency(110.0, inv_sample_rate); let mut freq_mod = SineOsc::new(); - freq_mod.set_frequency(8.0, inv_sample_rate); + freq_mod.set_frequency(1.0, inv_sample_rate); freq_mod.set_amplitude(1.0); let mut amp_mod = SineOsc::new(); From 1d2f97b4e226871014c2cf470070343df15d74a0 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 9 Dec 2022 11:02:16 +0100 Subject: [PATCH 196/374] Fixed erase bug --- embassy-nrf/src/nvmc.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index ba6a5912..405ea317 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -72,18 +72,13 @@ impl<'d> Nvmc<'d> { } #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] - fn erase_page(&mut self, page: u32) { - Self::regs().erasepage().write(|w| unsafe { w.bits(page) }); + fn erase_page(&mut self, page_addr: u32) { + Self::regs().erasepage().write(|w| unsafe { w.bits(page_addr) }); } #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] - fn erase_page(&mut self, page: u32) { - #[cfg(not(feature = "_nrf5340-net"))] - const FLASH_START_ADDR: u32 = 0; - #[cfg(feature = "_nrf5340-net")] - const FLASH_START_ADDR: u32 = 0x100_0000; - - let first_page_word = (FLASH_START_ADDR + page * PAGE_SIZE as u32) as *mut u32; + fn erase_page(&mut self, page_addr: u32) { + let first_page_word = page_addr as *mut u32; unsafe { first_page_word.write_volatile(0xFFFF_FFFF); } @@ -150,8 +145,8 @@ impl<'d> NorFlash for Nvmc<'d> { self.enable_erase(); self.wait_ready(); - for page in (from..to).step_by(PAGE_SIZE) { - self.erase_page(page); + for page_addr in (from..to).step_by(PAGE_SIZE) { + self.erase_page(page_addr); self.wait_ready(); } From 236d1048444fbaeddf1210d237bf9ab319f60079 Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Fri, 9 Dec 2022 14:26:09 +0100 Subject: [PATCH 197/374] embassy-stm32: add rs485 driver enable to uart --- embassy-stm32/build.rs | 2 ++ embassy-stm32/src/usart/buffered.rs | 27 +++++++++++++++++++++++++++ embassy-stm32/src/usart/mod.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index a4709f4c..ddfa97b2 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -244,11 +244,13 @@ fn main() { (("usart", "CTS"), quote!(crate::usart::CtsPin)), (("usart", "RTS"), quote!(crate::usart::RtsPin)), (("usart", "CK"), quote!(crate::usart::CkPin)), + (("usart", "DE"), quote!(crate::usart::DePin)), (("lpuart", "TX"), quote!(crate::usart::TxPin)), (("lpuart", "RX"), quote!(crate::usart::RxPin)), (("lpuart", "CTS"), quote!(crate::usart::CtsPin)), (("lpuart", "RTS"), quote!(crate::usart::RtsPin)), (("lpuart", "CK"), quote!(crate::usart::CkPin)), + (("lpuart", "DE"), quote!(crate::usart::DePin)), (("spi", "SCK"), quote!(crate::spi::SckPin)), (("spi", "MOSI"), quote!(crate::spi::MosiPin)), (("spi", "MISO"), quote!(crate::spi::MisoPin)), diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index d024bedc..874af1d7 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -89,6 +89,33 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) } + #[cfg(not(usart_v1))] + pub fn new_with_de( + state: &'d mut State<'d, T>, + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + de: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> BufferedUart<'d, T> { + into_ref!(de); + + T::enable(); + T::reset(); + + unsafe { + de.set_as_af(de.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_dem(true); + }); + } + + Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + } + fn new_inner( state: &'d mut State<'d, T>, _peri: impl Peripheral

+ 'd, diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index aea054a4..ea75361f 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -646,6 +646,31 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } + #[cfg(not(usart_v1))] + pub fn new_with_de( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + de: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(de); + + T::enable(); + T::reset(); + + unsafe { + de.set_as_af(de.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_dem(true); + }); + } + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + } + fn new_inner( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -1040,6 +1065,7 @@ pin_trait!(TxPin, BasicInstance); pin_trait!(CtsPin, BasicInstance); pin_trait!(RtsPin, BasicInstance); pin_trait!(CkPin, BasicInstance); +pin_trait!(DePin, BasicInstance); dma_trait!(TxDma, BasicInstance); dma_trait!(RxDma, BasicInstance); From 35db6e639bf5de017551164e9cbfaa2e5e46fca3 Mon Sep 17 00:00:00 2001 From: Simon Berg Date: Wed, 27 Jul 2022 22:45:46 +0200 Subject: [PATCH 198/374] PIO support for RPi Pico --- embassy-rp/Cargo.toml | 8 + embassy-rp/src/gpio.rs | 42 +- embassy-rp/src/lib.rs | 9 + embassy-rp/src/pio.rs | 1250 ++++++++++++++++++++++++++++++ embassy-rp/src/pio_instr_util.rs | 90 +++ examples/rp/Cargo.toml | 7 +- examples/rp/src/bin/pio_async.rs | 105 +++ examples/rp/src/bin/pio_dma.rs | 67 ++ 8 files changed, 1576 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/pio.rs create mode 100644 embassy-rp/src/pio_instr_util.rs create mode 100644 examples/rp/src/bin/pio_async.rs create mode 100644 examples/rp/src/bin/pio_dma.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 284d458c..75187571 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -26,6 +26,7 @@ time-driver = [] rom-func-cache = [] intrinsics = [] rom-v2-intrinsics = [] +pio = ["dep:pio", "dep:pio-proc"] # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] @@ -64,3 +65,10 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} + +paste = "1.0" +pio-proc = {version= "0.2", optional = true} +pio = {version= "0.2", optional = true} + +[patch.crates-io] +pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 930de206..315cd2ac 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -48,6 +48,21 @@ pub enum Pull { Down, } +/// Drive strength of an output +#[derive(Debug, Eq, PartialEq)] +pub enum Drive { + _2mA, + _4mA, + _8mA, + _12mA, +} +/// Slew rate of an output +#[derive(Debug, Eq, PartialEq)] +pub enum SlewRate { + Fast, + Slow, +} + /// A GPIO bank with up to 32 pins. #[derive(Debug, Eq, PartialEq)] pub enum Bank { @@ -459,7 +474,7 @@ impl<'d, T: Pin> Flex<'d, T> { #[inline] pub fn set_pull(&mut self, pull: Pull) { unsafe { - self.pin.pad_ctrl().write(|w| { + self.pin.pad_ctrl().modify(|w| { w.set_ie(true); match pull { Pull::Up => w.set_pue(true), @@ -470,6 +485,31 @@ impl<'d, T: Pin> Flex<'d, T> { } } + /// Set the pin's drive strength. + #[inline] + pub fn set_drive_strength(&mut self, strength: Drive) { + unsafe { + self.pin.pad_ctrl().modify(|w| { + w.set_drive(match strength { + Drive::_2mA => pac::pads::vals::Drive::_2MA, + Drive::_4mA => pac::pads::vals::Drive::_4MA, + Drive::_8mA => pac::pads::vals::Drive::_8MA, + Drive::_12mA => pac::pads::vals::Drive::_12MA, + }); + }); + } + } + + // Set the pin's slew rate. + #[inline] + pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { + unsafe { + self.pin.pad_ctrl().modify(|w| { + w.set_slewfast(slew_rate == SlewRate::Fast); + }); + } + } + /// Put the pin into input mode. /// /// The pull setting is left unchanged. diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d21b5f7b..67eb29d5 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -12,6 +12,12 @@ pub mod dma; pub mod gpio; pub mod i2c; pub mod interrupt; + +#[cfg(feature = "pio")] +pub mod pio; +#[cfg(feature = "pio")] +pub mod pio_instr_util; + pub mod rom_data; pub mod rtc; pub mod spi; @@ -102,6 +108,9 @@ embassy_hal_common::peripherals! { FLASH, ADC, + + PIO0, + PIO1, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs new file mode 100644 index 00000000..f049a16e --- /dev/null +++ b/embassy-rp/src/pio.rs @@ -0,0 +1,1250 @@ +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin as FuturePin; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; + +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_hal_common::PeripheralRef; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::dma::{self, Channel, Transfer}; +use crate::gpio::sealed::Pin as SealedPin; +use crate::gpio::{Drive, Pin, Pull, SlewRate}; +use crate::pac::dma::vals::{DataSize, TreqSel}; +use crate::{interrupt, pac, peripherals}; + +const PIOS: [&pac::pio::Pio; 2] = [&pac::PIO0, &pac::PIO1]; +const NEW_AW: AtomicWaker = AtomicWaker::new(); +const PIO_WAKERS_INIT: [AtomicWaker; 4] = [NEW_AW; 4]; +static FIFO_OUT_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; +static FIFO_IN_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; +static IRQ_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; + +pub enum FifoJoin { + /// Both TX and RX fifo is enabled + Duplex, + /// Rx fifo twice as deep. TX fifo disabled + RxOnly, + /// Tx fifo twice as deep. RX fifo disabled + TxOnly, +} + +#[derive(PartialEq)] +pub enum ShiftDirection { + Right = 1, + Left = 0, +} + +const RXNEMPTY_MASK: u32 = 1 << 0; +const TXNFULL_MASK: u32 = 1 << 4; +const SMIRQ_MASK: u32 = 1 << 8; + +#[interrupt] +unsafe fn PIO0_IRQ_1() { + use crate::pac; + let ints = pac::PIO0.irqs(1).ints().read().0; + let inte = pac::PIO0.irqs(1).inte(); + for i in 0..4 { + // Check RXNEMPTY + if ints & (RXNEMPTY_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(RXNEMPTY_MASK << i); + }); + FIFO_IN_WAKERS[0][i].wake(); + } + // Check IRQ flgs + if ints & (SMIRQ_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(SMIRQ_MASK << i); + }); + IRQ_WAKERS[0][i].wake(); + } + } +} + +#[interrupt] +unsafe fn PIO1_IRQ_1() { + use crate::pac; + let ints = pac::PIO1.irqs(1).ints().read().0; + let inte = pac::PIO1.irqs(1).inte(); + for i in 0..4 { + // Check all RXNEMPTY + if ints & (RXNEMPTY_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(RXNEMPTY_MASK << i); + }); + FIFO_IN_WAKERS[1][i].wake(); + } + // Check IRQ flgs + if ints & (SMIRQ_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(SMIRQ_MASK << i); + }); + IRQ_WAKERS[1][i].wake(); + } + } +} + +#[interrupt] +unsafe fn PIO0_IRQ_0() { + use crate::pac; + let ints = pac::PIO0.irqs(0).ints().read().0; + let inte = pac::PIO0.irqs(0).inte(); + //debug!("!{:04x}",ints); + // Check all TXNFULL + for i in 0..4 { + if ints & (TXNFULL_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(TXNFULL_MASK << i); + }); + FIFO_OUT_WAKERS[0][i].wake(); + } + } +} + +#[interrupt] +unsafe fn PIO1_IRQ_0() { + use crate::pac; + let ints = pac::PIO1.irqs(0).ints().read().0; + let inte = pac::PIO1.irqs(0).inte(); + // Check all TXNFULL + for i in 0..4 { + if ints & (TXNFULL_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(TXNFULL_MASK << i); + }); + FIFO_OUT_WAKERS[1][i].wake(); + } + } +} + +/// Future that waits for TX-FIFO to become writable +pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { + sm: &'a mut SM, + pio: PhantomData, + value: u32, +} + +impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, SM> { + pub fn new(sm: &'a mut SM, value: u32) -> Self { + unsafe { + critical_section::with(|_| { + let irq = PIO::IrqOut::steal(); + irq.set_priority(interrupt::Priority::P3); + + irq.enable(); + }); + } + FifoOutFuture { + sm, + pio: PhantomData::default(), + value, + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture<'d, PIO, SM> { + type Output = (); + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + //debug!("Poll {},{}", PIO::PIO_NO, SM); + let value = self.value; + if self.get_mut().sm.try_push_tx(value) { + Poll::Ready(()) + } else { + FIFO_OUT_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); + unsafe { + let irq = PIO::IrqOut::steal(); + irq.disable(); + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { + m.0 |= TXNFULL_MASK << SM::Sm::SM_NO; + }); + }); + irq.enable(); + } + // debug!("Pending"); + Poll::Pending + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { + fn drop(&mut self) { + unsafe { + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { + m.0 &= !(TXNFULL_MASK << SM::Sm::SM_NO); + }); + }); + } + } +} + +/// Future that waits for RX-FIFO to become readable +pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { + sm: &'a mut SM, + pio: PhantomData, +} + +impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { + pub fn new(sm: &'a mut SM) -> Self { + unsafe { + critical_section::with(|_| { + let irq = PIO::IrqIn::steal(); + irq.set_priority(interrupt::Priority::P3); + + irq.enable(); + }); + } + FifoInFuture { + sm, + pio: PhantomData::default(), + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, SM> { + type Output = u32; + fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + //debug!("Poll {},{}", PIO::PIO_NO, SM); + if let Some(v) = self.sm.try_pull_rx() { + Poll::Ready(v) + } else { + FIFO_IN_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); + unsafe { + let irq = PIO::IrqIn::steal(); + irq.disable(); + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 |= RXNEMPTY_MASK << SM::Sm::SM_NO; + }); + }); + irq.enable(); + } + //debug!("Pending"); + Poll::Pending + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, SM> { + fn drop(&mut self) { + unsafe { + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 &= !(RXNEMPTY_MASK << SM::Sm::SM_NO); + }); + }); + } + } +} + +/// Future that waits for IRQ +pub struct IrqFuture { + pio: PhantomData, + irq_no: u8, +} + +impl<'a, PIO: PioInstance> IrqFuture { + pub fn new(irq_no: u8) -> Self { + unsafe { + critical_section::with(|_| { + let irq = PIO::IrqSm::steal(); + irq.set_priority(interrupt::Priority::P3); + + irq.enable(); + }); + } + IrqFuture { + pio: PhantomData::default(), + irq_no, + } + } +} + +impl<'d, PIO: PioInstance> Future for IrqFuture { + type Output = (); + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + //debug!("Poll {},{}", PIO::PIO_NO, SM); + + // Check if IRQ flag is already set + if critical_section::with(|_| unsafe { + let irq_flags = PIOS[PIO::PIO_NO as usize].irq(); + if irq_flags.read().0 & (1 << self.irq_no) != 0 { + irq_flags.write(|m| { + m.0 = 1 << self.irq_no; + }); + true + } else { + false + } + }) { + return Poll::Ready(()); + } + + IRQ_WAKERS[PIO::PIO_NO as usize][self.irq_no as usize].register(cx.waker()); + unsafe { + let irq = PIO::IrqSm::steal(); + irq.disable(); + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 |= SMIRQ_MASK << self.irq_no; + }); + }); + irq.enable(); + } + Poll::Pending + } +} + +impl<'d, PIO: PioInstance> Drop for IrqFuture { + fn drop(&mut self) { + unsafe { + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 &= !(SMIRQ_MASK << self.irq_no); + }); + }); + } + } +} + +pub struct PioPin { + pin_bank: u8, + pio: PhantomData, +} + +impl PioPin { + /// Set the pin's drive strength. + #[inline] + pub fn set_drive_strength(&mut self, strength: Drive) { + unsafe { + self.pad_ctrl().modify(|w| { + w.set_drive(match strength { + Drive::_2mA => pac::pads::vals::Drive::_2MA, + Drive::_4mA => pac::pads::vals::Drive::_4MA, + Drive::_8mA => pac::pads::vals::Drive::_8MA, + Drive::_12mA => pac::pads::vals::Drive::_12MA, + }); + }); + } + } + + // Set the pin's slew rate. + #[inline] + pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { + unsafe { + self.pad_ctrl().modify(|w| { + w.set_slewfast(slew_rate == SlewRate::Fast); + }); + } + } + + /// Set the pin's pull. + #[inline] + pub fn set_pull(&mut self, pull: Pull) { + unsafe { + self.pad_ctrl().modify(|w| match pull { + Pull::Up => w.set_pue(true), + Pull::Down => w.set_pde(true), + Pull::None => {} + }); + } + } + + /// Set the pin's pull. + #[inline] + pub fn set_schmitt(&mut self, enable: bool) { + unsafe { + self.pad_ctrl().modify(|w| { + w.set_schmitt(enable); + }); + } + } + + pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { + let mask = 1 << self.pin(); + unsafe { + PIOS[PIO::PIO_NO as usize] + .input_sync_bypass() + .modify(|w| *w = if bypass { *w & !mask } else { *w | mask }); + } + } + + pub fn pin(&self) -> u8 { + self._pin() + } +} + +impl SealedPin for PioPin { + fn pin_bank(&self) -> u8 { + self.pin_bank + } +} + +pub struct PioStateMachineInstance { + pio: PhantomData, + sm: PhantomData, +} + +impl PioStateMachine for PioStateMachineInstance { + type Pio = PIO; + type Sm = SM; +} + +pub trait PioStateMachine: Sized + Unpin { + type Pio: PioInstance; + type Sm: SmInstance; + fn pio_no(&self) -> u8 { + let _ = self; + Self::Pio::PIO_NO + } + + fn sm_no(&self) -> u8 { + Self::Sm::SM_NO + } + + fn restart(&mut self) { + let _ = self; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .ctrl() + .modify(|w| w.set_sm_restart(1u8 << Self::Sm::SM_NO)); + } + } + fn set_enable(&mut self, enable: bool) { + let _ = self; + let mask = 1u8 << Self::Sm::SM_NO; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .ctrl() + .modify(|w| w.set_sm_enable((w.sm_enable() & !mask) | (if enable { mask } else { 0 }))); + } + } + + fn is_enabled(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } + } + + fn is_tx_empty(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } + } + fn is_tx_full(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } + } + + fn is_rx_empty(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } + } + fn is_rx_full(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } + } + + fn tx_level(&self) -> u8 { + unsafe { + let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; + (flevel >> (Self::Sm::SM_NO * 8)) as u8 & 0x0f + } + } + + fn rx_level(&self) -> u8 { + unsafe { + let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; + (flevel >> (Self::Sm::SM_NO * 8 + 4)) as u8 & 0x0f + } + } + + fn push_tx(&mut self, v: u32) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .txf(Self::Sm::SM_NO as usize) + .write_value(v); + } + } + + fn try_push_tx(&mut self, v: u32) -> bool { + if self.is_tx_full() { + return false; + } + self.push_tx(v); + true + } + + fn pull_rx(&mut self) -> u32 { + unsafe { PIOS[Self::Pio::PIO_NO as usize].rxf(Self::Sm::SM_NO as usize).read() } + } + + fn try_pull_rx(&mut self) -> Option { + if self.is_rx_empty() { + return None; + } + Some(self.pull_rx()) + } + + fn set_clkdiv(&mut self, div_x_256: u32) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .clkdiv() + .write(|w| w.0 = div_x_256 << 8); + } + } + + fn get_clkdiv(&self) -> u32 { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .clkdiv() + .read() + .0 + >> 8 + } + } + + fn clkdiv_restart(&mut self) { + let _ = self; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .ctrl() + .modify(|w| w.set_clkdiv_restart(1u8 << Self::Sm::SM_NO)); + } + } + + fn set_side_enable(&self, enable: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| w.set_side_en(enable)); + } + } + + fn is_side_enabled(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read() + .side_en() + } + } + + fn set_side_pindir(&mut self, pindir: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| w.set_side_pindir(pindir)); + } + } + + fn is_side_pindir(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read() + .side_pindir() + } + } + + fn set_jmp_pin(&mut self, pin: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| w.set_jmp_pin(pin)); + } + } + + fn get_jmp_pin(&mut self) -> u8 { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read() + .jmp_pin() + } + } + + fn set_wrap(&self, source: u8, target: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| { + w.set_wrap_top(source); + w.set_wrap_bottom(target) + }); + } + } + + /// Get wrapping addresses. Returns (source, target). + fn get_wrap(&self) -> (u8, u8) { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read(); + (r.wrap_top(), r.wrap_bottom()) + } + } + + fn set_fifo_join(&mut self, join: FifoJoin) { + let (rx, tx) = match join { + FifoJoin::Duplex => (false, false), + FifoJoin::RxOnly => (true, false), + FifoJoin::TxOnly => (false, true), + }; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| { + w.set_fjoin_rx(rx); + w.set_fjoin_tx(tx) + }); + } + } + fn get_fifo_join(&self) -> FifoJoin { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read(); + // Ignores the invalid state when both bits are set + if r.fjoin_rx() { + FifoJoin::RxOnly + } else if r.fjoin_tx() { + FifoJoin::TxOnly + } else { + FifoJoin::Duplex + } + } + } + + fn clear_fifos(&mut self) { + // Toggle FJOIN_RX to flush FIFOs + unsafe { + let shiftctrl = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl(); + shiftctrl.modify(|w| { + w.set_fjoin_rx(!w.fjoin_rx()); + }); + shiftctrl.modify(|w| { + w.set_fjoin_rx(!w.fjoin_rx()); + }); + } + } + + fn set_pull_threshold(&mut self, threshold: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_pull_thresh(threshold)); + } + } + + fn get_pull_threshold(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read(); + r.pull_thresh() + } + } + fn set_push_threshold(&mut self, threshold: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_push_thresh(threshold)); + } + } + + fn get_push_threshold(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read(); + r.push_thresh() + } + } + + fn set_out_shift_dir(&mut self, dir: ShiftDirection) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); + } + } + fn get_out_shiftdir(&self) -> ShiftDirection { + unsafe { + if PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .out_shiftdir() + { + ShiftDirection::Right + } else { + ShiftDirection::Left + } + } + } + + fn set_in_shift_dir(&mut self, dir: ShiftDirection) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); + } + } + fn get_in_shiftdir(&self) -> ShiftDirection { + unsafe { + if PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .in_shiftdir() + { + ShiftDirection::Right + } else { + ShiftDirection::Left + } + } + } + + fn set_autopull(&mut self, auto: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_autopull(auto)); + } + } + + fn is_autopull(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .autopull() + } + } + + fn set_autopush(&mut self, auto: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_autopush(auto)); + } + } + + fn is_autopush(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .autopush() + } + } + + fn get_addr(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .addr() + .read(); + r.addr() + } + } + fn set_sideset_count(&mut self, count: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| w.set_sideset_count(count)); + } + } + + fn get_sideset_count(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + r.sideset_count() + } + } + + fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + unsafe { + pin.io().ctrl().write(|w| { + w.set_funcsel( + if Self::Pio::PIO_NO == 1 { + pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 + } else { + // PIO == 0 + pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 + } + .0, + ); + }); + } + PioPin { + pin_bank: pin.pin_bank(), + pio: PhantomData::default(), + } + } + + fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| w.set_sideset_base(base_pin.pin())); + } + } + + fn get_sideset_base(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + r.sideset_base() + } + } + + /// Set the range of out pins affected by a set instruction. + fn set_set_range(&mut self, base: u8, count: u8) { + assert!(base + count < 32); + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| { + w.set_set_base(base); + w.set_set_count(count) + }); + } + } + + /// Get the range of out pins affected by a set instruction. Returns (base, count). + fn get_set_range(&self) -> (u8, u8) { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + (r.set_base(), r.set_count()) + } + } + + fn set_in_base_pin(&mut self, base: &PioPin) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| w.set_in_base(base.pin())); + } + } + + fn get_in_base(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + r.in_base() + } + } + + fn set_out_range(&mut self, base: u8, count: u8) { + assert!(base + count < 32); + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| { + w.set_out_base(base); + w.set_out_count(count) + }); + } + } + + /// Get the range of out pins affected by a set instruction. Returns (base, count). + fn get_out_range(&self) -> (u8, u8) { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + (r.out_base(), r.out_count()) + } + } + + fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin]) { + let count = pins.len(); + assert!(count >= 1); + let start = pins[0].pin() as usize; + assert!(start + pins.len() <= 32); + for i in 0..count { + assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); + } + self.set_out_range(start as u8, count as u8); + } + + fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin]) { + let count = pins.len(); + assert!(count >= 1); + let start = pins[0].pin() as usize; + assert!(start + pins.len() <= 32); + for i in 0..count { + assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); + } + self.set_set_range(start as u8, count as u8); + } + + fn get_current_instr() -> u32 { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .instr() + .read() + .0 + } + } + + fn exec_instr(&mut self, instr: u16) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .instr() + .write(|w| w.set_instr(instr)); + } + } + + fn write_instr(&mut self, start: usize, instrs: &[u16]) { + let _ = self; + write_instr( + Self::Pio::PIO_NO, + start, + instrs, + MEM_USED_BY_STATEMACHINE | Self::Sm::SM_NO as u32, + ); + } + + fn is_irq_set(&self, irq_no: u8) -> bool { + assert!(irq_no < 8); + unsafe { + let irq_flags = PIOS[Self::Pio::PIO_NO as usize].irq(); + irq_flags.read().0 & (1 << irq_no) != 0 + } + } + + fn clear_irq(&mut self, irq_no: usize) { + assert!(irq_no < 8); + unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } + } + + fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { + FifoOutFuture::new(self, value) + } + + fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, Self::Pio, Self> { + FifoInFuture::new(self) + } + + fn wait_irq(&self, irq_no: u8) -> IrqFuture { + IrqFuture::new(irq_no) + } + + fn has_tx_stalled(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().txstall() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_txstall(1 << Self::Sm::SM_NO)); + ret + } + } + + fn has_tx_overflowed(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().txover() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_txover(1 << Self::Sm::SM_NO)); + ret + } + } + + fn has_rx_stalled(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().rxstall() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_rxstall(1 << Self::Sm::SM_NO)); + ret + } + } + + fn has_rx_underflowed(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().rxunder() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_rxunder(1 << Self::Sm::SM_NO)); + ret + } + } + + fn dma_push<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [u32]) -> Transfer<'a, C> { + unsafe { + dma::init(); + let pio_no = Self::Pio::PIO_NO; + let sm_no = Self::Sm::SM_NO; + let p = ch.regs(); + p.read_addr().write_value(data.as_ptr() as u32); + p.write_addr() + .write_value(PIOS[pio_no as usize].txf(sm_no as usize).ptr() as u32); + p.trans_count().write_value(data.len() as u32); + p.ctrl_trig().write(|w| { + // Set TX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); + w.set_data_size(DataSize::SIZE_WORD); + w.set_chain_to(ch.number()); + w.set_incr_read(true); + w.set_incr_write(false); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) + } + + fn dma_pull<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [u32]) -> Transfer<'a, C> { + unsafe { + dma::init(); + let pio_no = Self::Pio::PIO_NO; + let sm_no = Self::Sm::SM_NO; + let p = ch.regs(); + p.write_addr().write_value(data.as_ptr() as u32); + p.read_addr() + .write_value(PIOS[pio_no as usize].rxf(sm_no as usize).ptr() as u32); + p.trans_count().write_value(data.len() as u32); + p.ctrl_trig().write(|w| { + // Set TX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); + w.set_data_size(DataSize::SIZE_WORD); + w.set_chain_to(ch.number()); + w.set_incr_read(false); + w.set_incr_write(true); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) + } +} + +/* +This is a bit array containing 4 bits for every word in the PIO instruction memory. +*/ +// Bit 3-2 +//const MEM_USE_MASK: u32 = 0b1100; +const MEM_NOT_USED: u32 = 0b0000; +const MEM_USED_BY_STATEMACHINE: u32 = 0b0100; +const MEM_USED_BY_COMMON: u32 = 0b1000; + +// Bit 1-0 is the number of the state machine +//const MEM_STATE_MASK: u32 = 0b0011; + +// Should use mutex if running on multiple cores +static mut INSTR_MEM_STATUS: &'static mut [[u32; 4]; 2] = &mut [[0; 4]; 2]; + +fn instr_mem_get_status(pio_no: u8, addr: u8) -> u32 { + ((unsafe { INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }) >> ((addr & 0x07) * 4)) & 0xf +} + +fn instr_mem_set_status(pio_no: u8, addr: u8, status: u32) { + let w = unsafe { &mut INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }; + let shift = (addr & 0x07) * 4; + *w = (*w & !(0xf << shift)) | (status << shift); +} + +fn instr_mem_is_free(pio_no: u8, addr: u8) -> bool { + instr_mem_get_status(pio_no, addr) == MEM_NOT_USED +} + +pub struct PioCommonInstance { + pio: PhantomData, +} + +impl PioCommon for PioCommonInstance { + type Pio = PIO; +} + +fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { + for (i, instr) in instrs.iter().enumerate() { + let addr = (i + start) as u8; + assert!( + instr_mem_is_free(pio_no, addr), + "Trying to write already used PIO instruction memory at {}", + addr + ); + unsafe { + PIOS[pio_no as usize].instr_mem(addr as usize).write(|w| { + w.set_instr_mem(*instr); + }); + instr_mem_set_status(pio_no, addr, mem_user); + } + } +} + +pub trait PioCommon: Sized { + type Pio: PioInstance; + + fn write_instr(&mut self, start: usize, instrs: &[u16]) { + let _ = self; + write_instr(Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); + } + + fn clear_irq(&mut self, irq_no: usize) { + assert!(irq_no < 8); + unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } + } + + fn clear_irqs(&mut self, mask: u8) { + unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(mask)) } + } + + fn force_irq(&mut self, irq_no: usize) { + assert!(irq_no < 8); + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .irq_force() + .write(|w| w.set_irq_force(1 << irq_no)) + } + } + + fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .input_sync_bypass() + .modify(|w| *w = (*w & !mask) | (bypass & mask)); + } + } + + fn get_input_sync_bypass(&self) -> u32 { + unsafe { PIOS[Self::Pio::PIO_NO as usize].input_sync_bypass().read() } + } +} + +// Identifies a specific state machine inside a PIO device +pub struct SmInstanceBase {} + +pub trait SmInstance: Unpin { + const SM_NO: u8; +} + +impl SmInstance for SmInstanceBase { + const SM_NO: u8 = SM_NO; +} + +pub trait PioPeripherial: Sized { + type Pio: PioInstance; + fn pio(&self) -> u8 { + let _ = self; + Self::Pio::PIO_NO + } + + fn split( + self, + ) -> ( + PioCommonInstance, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, + ) { + let _ = self; + ( + PioCommonInstance { + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + ) + } +} + +// Identifies a specific PIO device +pub struct PioInstanceBase {} + +pub trait PioInstance: Unpin { + const PIO_NO: u8; + type IrqOut: Interrupt; + type IrqIn: Interrupt; + type IrqSm: Interrupt; +} + +impl PioInstance for PioInstanceBase<0> { + const PIO_NO: u8 = 0; + type IrqOut = interrupt::PIO0_IRQ_0; + type IrqIn = interrupt::PIO0_IRQ_1; + type IrqSm = interrupt::PIO0_IRQ_1; +} + +impl PioInstance for PioInstanceBase<1> { + const PIO_NO: u8 = 1; + type IrqOut = interrupt::PIO1_IRQ_0; + type IrqIn = interrupt::PIO1_IRQ_1; + type IrqSm = interrupt::PIO1_IRQ_1; +} + +pub type Pio0 = PioInstanceBase<0>; +pub type Pio1 = PioInstanceBase<1>; + +pub type Sm0 = SmInstanceBase<0>; +pub type Sm1 = SmInstanceBase<1>; +pub type Sm2 = SmInstanceBase<2>; +pub type Sm3 = SmInstanceBase<3>; + +macro_rules! impl_pio_sm { + ($name:ident, $pio:expr) => { + impl PioPeripherial for peripherals::$name { + type Pio = PioInstanceBase<$pio>; + } + }; +} + +impl_pio_sm!(PIO0, 0); +impl_pio_sm!(PIO1, 1); diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs new file mode 100644 index 00000000..ae26ff1d --- /dev/null +++ b/embassy-rp/src/pio_instr_util.rs @@ -0,0 +1,90 @@ +use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; + +use crate::pio::PioStateMachine; + +pub fn set_x(sm: &mut SM, value: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::X, + bit_count: 32, + } + .encode(); + sm.push_tx(value); + sm.exec_instr(OUT); +} + +pub fn get_x(sm: &mut SM) -> u32 { + const IN: u16 = InstructionOperands::IN { + source: InSource::X, + bit_count: 32, + } + .encode(); + sm.exec_instr(IN); + sm.pull_rx() +} + +pub fn set_y(sm: &mut SM, value: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::Y, + bit_count: 32, + } + .encode(); + sm.push_tx(value); + sm.exec_instr(OUT); +} + +pub fn get_y(sm: &mut SM) -> u32 { + const IN: u16 = InstructionOperands::IN { + source: InSource::Y, + bit_count: 32, + } + .encode(); + sm.exec_instr(IN); + + sm.pull_rx() +} + +pub fn set_pindir(sm: &mut SM, data: u8) { + let set: u16 = InstructionOperands::SET { + destination: SetDestination::PINDIRS, + data, + } + .encode(); + sm.exec_instr(set); +} + +pub fn set_pin(sm: &mut SM, data: u8) { + let set: u16 = InstructionOperands::SET { + destination: SetDestination::PINS, + data, + } + .encode(); + sm.exec_instr(set); +} + +pub fn set_out_pin(sm: &mut SM, data: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::PINS, + bit_count: 32, + } + .encode(); + sm.push_tx(data); + sm.exec_instr(OUT); +} +pub fn set_out_pindir(sm: &mut SM, data: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::PINDIRS, + bit_count: 32, + } + .encode(); + sm.push_tx(data); + sm.exec_instr(OUT); +} + +pub fn exec_jmp(sm: &mut SM, to_addr: u8) { + let jmp: u16 = InstructionOperands::JMP { + address: to_addr, + condition: JmpCondition::Always, + } + .encode(); + sm.exec_instr(jmp); +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 60a8ba94..b07c471a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.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", "time-driver"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", 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" } @@ -34,6 +34,11 @@ embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" log = "0.4" +pio-proc = "0.2" +pio = "0.2" [profile.release] debug = true + +[patch.crates-io] +pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs new file mode 100644 index 00000000..e9211db3 --- /dev/null +++ b/examples/rp/src/bin/pio_async.rs @@ -0,0 +1,105 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::gpio::{AnyPin, Pin}; +use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; +use embassy_rp::pio_instr_util; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { + // Setup sm0 + + // Send data serially to pin + let prg = pio_proc::pio_asm!( + ".origin 16", + "set pindirs, 1", + ".wrap_target", + "out pins,1 [19]", + ".wrap", + ); + + let origin = prg.program.origin.unwrap_or(0); + let out_pin = sm.make_pio_pin(pin); + let pio_pins = [&out_pin]; + sm.set_out_pins(&pio_pins); + sm.write_instr(origin as usize, &prg.program.code); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); + sm.set_set_range(0, 1); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + sm.set_autopull(true); + sm.set_out_shift_dir(ShiftDirection::Left); + + sm.set_enable(true); + + let mut v = 0x0f0caffa; + loop { + sm.wait_push(v).await; + v ^= 0xffff; + info!("Pushed {:032b} to FIFO", v); + } +} + +#[embassy_executor::task] +async fn pio_task_sm1(mut sm: PioStateMachineInstance) { + // Setupm sm1 + + // Read 0b10101 repeatedly until ISR is full + let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); + + let origin = prg.program.origin.unwrap_or(0); + sm.write_instr(origin as usize, &prg.program.code); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + sm.set_set_range(0, 0); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + sm.set_autopush(true); + sm.set_in_shift_dir(ShiftDirection::Right); + sm.set_enable(true); + loop { + let rx = sm.wait_pull().await; + info!("Pulled {:032b} from FIFO", rx); + } +} + +#[embassy_executor::task] +async fn pio_task_sm2(mut sm: PioStateMachineInstance) { + // Setup sm2 + + // Repeatedly trigger IRQ 3 + let prg = pio_proc::pio_asm!( + ".origin 0", + ".wrap_target", + "set x,10", + "delay:", + "jmp x-- delay [15]", + "irq 3 [15]", + ".wrap", + ); + let origin = prg.program.origin.unwrap_or(0); + + sm.write_instr(origin as usize, &prg.program.code); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + sm.set_enable(true); + loop { + sm.wait_irq(3).await; + info!("IRQ trigged"); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + + let (_, sm0, sm1, sm2, ..) = pio.split(); + + spawner.spawn(pio_task_sm0(sm0, p.PIN_0.degrade())).unwrap(); + spawner.spawn(pio_task_sm1(sm1)).unwrap(); + spawner.spawn(pio_task_sm2(sm2)).unwrap(); +} diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs new file mode 100644 index 00000000..bdcdf200 --- /dev/null +++ b/examples/rp/src/bin/pio_dma.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +use defmt::info; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; +use embassy_rp::{pio_instr_util, Peripheral}; +use {defmt_rtt as _, panic_probe as _}; + +fn swap_nibbles(v: u32) -> u32 { + let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; + let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; + (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16 +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let (_, mut sm, ..) = pio.split(); + + let prg = pio_proc::pio_asm!( + ".origin 0", + "set pindirs,1", + ".wrap_target", + "set y,7", + "loop:", + "out x,4", + "in x,4", + "jmp y--, loop", + ".wrap", + ); + + let origin = prg.program.origin.unwrap_or(0); + sm.write_instr(origin as usize, &prg.program.code); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + sm.set_autopull(true); + sm.set_autopush(true); + sm.set_pull_threshold(32); + sm.set_push_threshold(32); + sm.set_out_shift_dir(ShiftDirection::Right); + sm.set_in_shift_dir(ShiftDirection::Left); + + sm.set_enable(true); + + let mut dma_out_ref = p.DMA_CH0.into_ref(); + let mut dma_in_ref = p.DMA_CH1.into_ref(); + let mut dout = [0x12345678u32; 29]; + for i in 1..dout.len() { + dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; + } + let mut din = [0u32; 29]; + loop { + join( + sm.dma_push(dma_out_ref.reborrow(), &dout), + sm.dma_pull(dma_in_ref.reborrow(), &mut din), + ) + .await; + for i in 0..din.len() { + assert_eq!(din[i], swap_nibbles(dout[i])); + } + info!("Swapped {} words", dout.len()); + } +} From cd59046e6c179809ee19fb2592e65cf5a0a07980 Mon Sep 17 00:00:00 2001 From: Simon Berg Date: Tue, 6 Dec 2022 21:42:30 +0100 Subject: [PATCH 199/374] Added RelocateProgram class for adjusting PIO-programs for different origins. --- embassy-rp/src/lib.rs | 2 + embassy-rp/src/pio.rs | 19 +++++--- embassy-rp/src/relocate.rs | 77 ++++++++++++++++++++++++++++++++ examples/rp/src/bin/pio_async.rs | 31 ++++++++----- examples/rp/src/bin/pio_dma.rs | 10 +++-- 5 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 embassy-rp/src/relocate.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 67eb29d5..55139272 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -17,6 +17,8 @@ pub mod interrupt; pub mod pio; #[cfg(feature = "pio")] pub mod pio_instr_util; +#[cfg(feature = "pio")] +pub mod relocate; pub mod rom_data; pub mod rtc; diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index f049a16e..1a067e54 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -941,7 +941,10 @@ pub trait PioStateMachine: Sized + Unpin { } } - fn write_instr(&mut self, start: usize, instrs: &[u16]) { + fn write_instr(&mut self, start: usize, instrs: I) + where + I: Iterator, + { let _ = self; write_instr( Self::Pio::PIO_NO, @@ -1098,8 +1101,11 @@ impl PioCommon for PioCommonInstance { type Pio = PIO; } -fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { - for (i, instr) in instrs.iter().enumerate() { +fn write_instr(pio_no: u8, start: usize, instrs: I, mem_user: u32) +where + I: Iterator, +{ + for (i, instr) in instrs.enumerate() { let addr = (i + start) as u8; assert!( instr_mem_is_free(pio_no, addr), @@ -1108,7 +1114,7 @@ fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { ); unsafe { PIOS[pio_no as usize].instr_mem(addr as usize).write(|w| { - w.set_instr_mem(*instr); + w.set_instr_mem(instr); }); instr_mem_set_status(pio_no, addr, mem_user); } @@ -1118,7 +1124,10 @@ fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { pub trait PioCommon: Sized { type Pio: PioInstance; - fn write_instr(&mut self, start: usize, instrs: &[u16]) { + fn write_instr(&mut self, start: usize, instrs: I) + where + I: Iterator, + { let _ = self; write_instr(Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); } diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs new file mode 100644 index 00000000..f36170e0 --- /dev/null +++ b/embassy-rp/src/relocate.rs @@ -0,0 +1,77 @@ +use core::iter::Iterator; + +use pio::{Program, SideSet, Wrap}; + +pub struct CodeIterator<'a, I> +where + I: Iterator, +{ + iter: I, + offset: u8, +} + +impl<'a, I: Iterator> CodeIterator<'a, I> { + pub fn new(iter: I, offset: u8) -> CodeIterator<'a, I> { + CodeIterator { iter, offset } + } +} + +impl<'a, I> Iterator for CodeIterator<'a, I> +where + I: Iterator, +{ + type Item = u16; + fn next(&mut self) -> Option { + self.iter.next().and_then(|&instr| { + Some(if instr & 0b1110_0000_0000_0000 == 0 { + // this is a JMP instruction -> add offset to address + let address = (instr & 0b1_1111) as u8; + let address = address + self.offset; + assert!( + address < pio::RP2040_MAX_PROGRAM_SIZE as u8, + "Invalid JMP out of the program after offset addition" + ); + instr & (!0b11111) | address as u16 + } else { + instr + }) + }) + } +} + +pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { + program: &'a Program, + origin: u8, +} + +impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { + pub fn new(program: &Program) -> RelocatedProgram { + let origin = program.origin.unwrap_or(0); + RelocatedProgram { program, origin } + } + + pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram { + RelocatedProgram { program, origin } + } + + pub fn code(&'a self) -> CodeIterator<'a, core::slice::Iter<'a, u16>> { + CodeIterator::new(self.program.code.iter(), self.origin) + } + + pub fn wrap(&self) -> Wrap { + let wrap = self.program.wrap; + let origin = self.origin; + Wrap { + source: wrap.source + origin, + target: wrap.target + origin, + } + } + + pub fn side_set(&self) -> SideSet { + self.program.side_set + } + + pub fn origin(&self) -> u8 { + self.origin + } +} diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index e9211db3..45a8c73f 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -6,6 +6,7 @@ use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; use embassy_rp::pio_instr_util; +use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] @@ -21,15 +22,17 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { ".wrap", ); - let origin = prg.program.origin.unwrap_or(0); + let relocated = RelocatedProgram::new(&prg.program); let out_pin = sm.make_pio_pin(pin); let pio_pins = [&out_pin]; sm.set_out_pins(&pio_pins); - sm.write_instr(origin as usize, &prg.program.code); - pio_instr_util::exec_jmp(&mut sm, origin); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); sm.set_set_range(0, 1); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + sm.set_autopull(true); sm.set_out_shift_dir(ShiftDirection::Left); @@ -50,12 +53,14 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance) { // Read 0b10101 repeatedly until ISR is full let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); - let origin = prg.program.origin.unwrap_or(0); - sm.write_instr(origin as usize, &prg.program.code); - pio_instr_util::exec_jmp(&mut sm, origin); + let relocated = RelocatedProgram::new(&prg.program); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_set_range(0, 0); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + sm.set_autopush(true); sm.set_in_shift_dir(ShiftDirection::Right); sm.set_enable(true); @@ -79,11 +84,13 @@ async fn pio_task_sm2(mut sm: PioStateMachineInstance) { "irq 3 [15]", ".wrap", ); - let origin = prg.program.origin.unwrap_or(0); + let relocated = RelocatedProgram::new(&prg.program); + sm.write_instr(relocated.origin() as usize, relocated.code()); - sm.write_instr(origin as usize, &prg.program.code); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); - pio_instr_util::exec_jmp(&mut sm, origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_enable(true); loop { diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index bdcdf200..b19ef408 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -5,6 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; +use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; @@ -32,11 +33,12 @@ async fn main(_spawner: Spawner) { ".wrap", ); - let origin = prg.program.origin.unwrap_or(0); - sm.write_instr(origin as usize, &prg.program.code); - pio_instr_util::exec_jmp(&mut sm, origin); + let relocated = RelocatedProgram::new(&prg.program); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); sm.set_autopull(true); sm.set_autopush(true); sm.set_pull_threshold(32); From 488e3224783abe07b2be8046642fd9b81f5714b9 Mon Sep 17 00:00:00 2001 From: Simon Berg Date: Fri, 9 Dec 2022 20:30:17 +0100 Subject: [PATCH 200/374] Clear unused pulls in set_pull. --- embassy-rp/src/gpio.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 315cd2ac..ec05de61 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -476,11 +476,13 @@ impl<'d, T: Pin> Flex<'d, T> { unsafe { self.pin.pad_ctrl().modify(|w| { w.set_ie(true); - match pull { - Pull::Up => w.set_pue(true), - Pull::Down => w.set_pde(true), - Pull::None => {} - } + let (pu, pd) = match pull { + Pull::Up => (true, false), + Pull::Down => (false, true), + Pull::None => (false, false), + }; + w.set_pue(pu); + w.set_pde(pd); }); } } From 5d4f09156af094732edc5c01332af8b13db10e0f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Dec 2022 23:33:53 +0100 Subject: [PATCH 201/374] nrf: add manually_create_executor example. --- .../nrf/src/bin/manually_create_executor.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/nrf/src/bin/manually_create_executor.rs diff --git a/examples/nrf/src/bin/manually_create_executor.rs b/examples/nrf/src/bin/manually_create_executor.rs new file mode 100644 index 00000000..12ce660f --- /dev/null +++ b/examples/nrf/src/bin/manually_create_executor.rs @@ -0,0 +1,49 @@ +// This example showcases how to manually create an executor. +// This is what the #[embassy::main] macro does behind the scenes. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::{info, unwrap}; +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run1() { + loop { + info!("BIG INFREQUENT TICK"); + Timer::after(Duration::from_ticks(64000)).await; + } +} + +#[embassy_executor::task] +async fn run2() { + loop { + info!("tick"); + Timer::after(Duration::from_ticks(13000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let _p = embassy_nrf::init(Default::default()); + + // Create the executor and put it in a StaticCell, because `run` needs `&'static mut Executor`. + let executor = EXECUTOR.init(Executor::new()); + + // Run it. + // `run` calls the closure then runs the executor forever. It never returns. + executor.run(|spawner| { + // Here we get access to a spawner to spawn the initial tasks. + unwrap!(spawner.spawn(run1())); + unwrap!(spawner.spawn(run2())); + }); +} From 1ee58492fbc58b721dc5ed9037c6787af257cbeb Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 08:26:35 +0100 Subject: [PATCH 202/374] embassy-rp: Add multicore support --- embassy-rp/Cargo.toml | 2 +- embassy-rp/src/critical_section_impl.rs | 142 +++++++++++++ embassy-rp/src/lib.rs | 2 + embassy-rp/src/multicore.rs | 266 ++++++++++++++++++++++++ examples/rp/Cargo.toml | 3 +- examples/rp/src/bin/multicore.rs | 62 ++++++ 6 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/critical_section_impl.rs create mode 100644 embassy-rp/src/multicore.rs create mode 100644 examples/rp/src/bin/multicore.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 284d458c..07cd1bc1 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -50,7 +50,7 @@ nb = "1.0.0" cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -critical-section = "1.1" +critical-section = { version = "1.1", features = ["restore-state-u8"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true } diff --git a/embassy-rp/src/critical_section_impl.rs b/embassy-rp/src/critical_section_impl.rs new file mode 100644 index 00000000..ce284c85 --- /dev/null +++ b/embassy-rp/src/critical_section_impl.rs @@ -0,0 +1,142 @@ +use core::sync::atomic::{AtomicU8, Ordering}; + +use crate::pac; + +struct RpSpinlockCs; +critical_section::set_impl!(RpSpinlockCs); + +/// Marker value to indicate no-one has the lock. +/// +/// Initialising `LOCK_OWNER` to 0 means cheaper static initialisation so it's the best choice +const LOCK_UNOWNED: u8 = 0; + +/// Indicates which core owns the lock so that we can call critical_section recursively. +/// +/// 0 = no one has the lock, 1 = core0 has the lock, 2 = core1 has the lock +static LOCK_OWNER: AtomicU8 = AtomicU8::new(LOCK_UNOWNED); + +/// Marker value to indicate that we already owned the lock when we started the `critical_section`. +/// +/// Since we can't take the spinlock when we already have it, we need some other way to keep track of `critical_section` ownership. +/// `critical_section` provides a token for communicating between `acquire` and `release` so we use that. +/// If we're the outermost call to `critical_section` we use the values 0 and 1 to indicate we should release the spinlock and set the interrupts back to disabled and enabled, respectively. +/// The value 2 indicates that we aren't the outermost call, and should not release the spinlock or re-enable interrupts in `release` +const LOCK_ALREADY_OWNED: u8 = 2; + +unsafe impl critical_section::Impl for RpSpinlockCs { + unsafe fn acquire() -> u8 { + RpSpinlockCs::acquire() + } + + unsafe fn release(token: u8) { + RpSpinlockCs::release(token); + } +} + +impl RpSpinlockCs { + unsafe fn acquire() -> u8 { + // Store the initial interrupt state and current core id in stack variables + let interrupts_active = cortex_m::register::primask::read().is_active(); + // We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1. + let core = pac::SIO.cpuid().read() as u8 + 1; + // Do we already own the spinlock? + if LOCK_OWNER.load(Ordering::Acquire) == core { + // We already own the lock, so we must have called acquire within a critical_section. + // Return the magic inner-loop value so that we know not to re-enable interrupts in release() + LOCK_ALREADY_OWNED + } else { + // Spin until we get the lock + loop { + // Need to disable interrupts to ensure that we will not deadlock + // if an interrupt enters critical_section::Impl after we acquire the lock + cortex_m::interrupt::disable(); + // Ensure the compiler doesn't re-order accesses and violate safety here + core::sync::atomic::compiler_fence(Ordering::SeqCst); + // Read the spinlock reserved for `critical_section` + if let Some(lock) = Spinlock31::try_claim() { + // We just acquired the lock. + // 1. Forget it, so we don't immediately unlock + core::mem::forget(lock); + // 2. Store which core we are so we can tell if we're called recursively + LOCK_OWNER.store(core, Ordering::Relaxed); + break; + } + // We didn't get the lock, enable interrupts if they were enabled before we started + if interrupts_active { + cortex_m::interrupt::enable(); + } + } + // If we broke out of the loop we have just acquired the lock + // As the outermost loop, we want to return the interrupt status to restore later + interrupts_active as _ + } + } + + unsafe fn release(token: u8) { + // Did we already own the lock at the start of the `critical_section`? + if token != LOCK_ALREADY_OWNED { + // No, it wasn't owned at the start of this `critical_section`, so this core no longer owns it. + // Set `LOCK_OWNER` back to `LOCK_UNOWNED` to ensure the next critical section tries to obtain the spinlock instead + LOCK_OWNER.store(LOCK_UNOWNED, Ordering::Relaxed); + // Ensure the compiler doesn't re-order accesses and violate safety here + core::sync::atomic::compiler_fence(Ordering::SeqCst); + // Release the spinlock to allow others to enter critical_section again + Spinlock31::release(); + // Re-enable interrupts if they were enabled when we first called acquire() + // We only do this on the outermost `critical_section` to ensure interrupts stay disabled + // for the whole time that we have the lock + if token != 0 { + cortex_m::interrupt::enable(); + } + } + } +} + +pub struct Spinlock(core::marker::PhantomData<()>) +where + Spinlock: SpinlockValid; + +impl Spinlock +where + Spinlock: SpinlockValid, +{ + /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is + /// already in use somewhere else. + pub fn try_claim() -> Option { + // Safety: We're only reading from this register + unsafe { + let lock = pac::SIO.spinlock(N).read(); + if lock > 0 { + Some(Self(core::marker::PhantomData)) + } else { + None + } + } + } + + /// Clear a locked spin-lock. + /// + /// # Safety + /// + /// Only call this function if you hold the spin-lock. + pub unsafe fn release() { + unsafe { + // Write (any value): release the lock + pac::SIO.spinlock(N).write_value(1); + } + } +} + +impl Drop for Spinlock +where + Spinlock: SpinlockValid, +{ + fn drop(&mut self) { + // This is safe because we own the object, and hence hold the lock. + unsafe { Self::release() } + } +} + +pub(crate) type Spinlock31 = Spinlock<31>; +pub trait SpinlockValid {} +impl SpinlockValid for Spinlock<31> {} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d21b5f7b..9a2fb7fc 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -5,6 +5,7 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod critical_section_impl; mod intrinsics; pub mod adc; @@ -23,6 +24,7 @@ pub mod usb; pub mod clocks; pub mod flash; +pub mod multicore; mod reset; // Reexports diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs new file mode 100644 index 00000000..dd3b70a6 --- /dev/null +++ b/embassy-rp/src/multicore.rs @@ -0,0 +1,266 @@ +//! Multicore support +//! +//! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. +//! It provides functionality for setting up the stack, and starting core1. +//! +//! The entrypoint for core1 can be any function that never returns, including closures. + +use core::mem::ManuallyDrop; +use core::sync::atomic::{compiler_fence, Ordering}; + +use crate::pac; + +/// Errors for multicore operations. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Operation is invalid on this core. + InvalidCore, + /// Core was unresponsive to commands. + Unresponsive, +} + +/// Core ID +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreId { + Core0, + Core1, +} + +#[inline(always)] +fn install_stack_guard(stack_bottom: *mut usize) { + let core = unsafe { cortex_m::Peripherals::steal() }; + + // Trap if MPU is already configured + if core.MPU.ctrl.read() != 0 { + cortex_m::asm::udf(); + } + + // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will + // just shorten the valid stack range a tad. + let addr = (stack_bottom as u32 + 31) & !31; + // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want + let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); + unsafe { + core.MPU.ctrl.write(5); // enable mpu with background default map + core.MPU.rbar.write((addr & !0xff) | 0x8); + core.MPU.rasr.write( + 1 // enable region + | (0x7 << 1) // size 2^(7 + 1) = 256 + | (subregion_select << 8) + | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions + ); + } +} + +#[inline(always)] +fn core1_setup(stack_bottom: *mut usize) { + install_stack_guard(stack_bottom); + // TODO: irq priorities +} + +/// Multicore execution management. +pub struct Multicore { + cores: (Core, Core), +} + +/// Data type for a properly aligned stack of N 32-bit (usize) words +#[repr(C, align(32))] +pub struct Stack { + /// Memory to be used for the stack + pub mem: [usize; SIZE], +} + +impl Stack { + /// Construct a stack of length SIZE, initialized to 0 + pub const fn new() -> Stack { + Stack { mem: [0; SIZE] } + } +} + +impl Multicore { + /// Create a new |Multicore| instance. + pub fn new() -> Self { + Self { + cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), + } + } + + /// Get the available |Core| instances. + pub fn cores(&mut self) -> &mut (Core, Core) { + &mut self.cores + } +} + +/// A handle for controlling a logical core. +pub struct Core { + pub id: CoreId, +} + +impl Core { + /// Spawn a function on this core. + pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> + where + F: FnOnce() -> bad::Never + Send + 'static, + { + fn fifo_write(value: u32) { + unsafe { + let sio = pac::SIO; + // Wait for the FIFO to have some space + while !sio.fifo().st().read().rdy() { + cortex_m::asm::nop(); + } + // Signal that it's safe for core 0 to get rid of the original value now. + sio.fifo().wr().write_value(value); + } + + // Fire off an event to the other core. + // This is required as the other core may be `wfe` (waiting for event) + cortex_m::asm::sev(); + } + + fn fifo_read() -> u32 { + unsafe { + let sio = pac::SIO; + // Keep trying until FIFO has data + loop { + if sio.fifo().st().read().vld() { + return sio.fifo().rd().read(); + } else { + // We expect the sending core to `sev` on write. + cortex_m::asm::wfe(); + } + } + } + } + + fn fifo_drain() { + unsafe { + let sio = pac::SIO; + while sio.fifo().st().read().vld() { + let _ = sio.fifo().rd().read(); + } + } + } + + match self.id { + CoreId::Core1 => { + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + let entry = unsafe { ManuallyDrop::take(entry) }; + // Signal that it's safe for core 0 to get rid of the original value now. + fifo_write(1); + entry() + } + + // Reset the core + unsafe { + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); + } + psm.frce_off().modify(|w| w.set_proc1(false)); + } + + // Set up the stack + let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } + + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + + let p = unsafe { cortex_m::Peripherals::steal() }; + let vector_table = p.SCB.vtor.read(); + + // After reset, core 1 is waiting to receive commands over FIFO. + // This is the sequence to have it jump to some code. + let cmd_seq = [ + 0, + 0, + 1, + vector_table as usize, + stack_ptr as usize, + core1_startup:: as usize, + ]; + + let mut seq = 0; + let mut fails = 0; + loop { + let cmd = cmd_seq[seq] as u32; + if cmd == 0 { + fifo_drain(); + cortex_m::asm::sev(); + } + fifo_write(cmd); + + let response = fifo_read(); + if cmd == response { + seq += 1; + } else { + seq = 0; + fails += 1; + if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint, + // so we have to drop it ourselves. + drop(ManuallyDrop::into_inner(entry)); + return Err(Error::Unresponsive); + } + } + if seq >= cmd_seq.len() { + break; + } + } + + // Wait until the other core has copied `entry` before returning. + fifo_read(); + + Ok(()) + } + _ => Err(Error::InvalidCore), + } + } +} + +// https://github.com/nvzqz/bad-rs/blob/master/src/never.rs +mod bad { + pub(crate) type Never = ::Output; + + pub trait HasOutput { + type Output; + } + + impl HasOutput for fn() -> O { + type Output = O; + } + + type F = fn() -> !; +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 60a8ba94..bd624a32 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -18,7 +18,8 @@ embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs new file mode 100644 index 00000000..46d3cd17 --- /dev/null +++ b/examples/rp/src/bin/multicore.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::PIN_25; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use embassy_time::{Duration, Timer}; +use embassy_rp::multicore::{Multicore, Stack}; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<4096> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL: Channel = Channel::new(); + +enum LedState { + On, + Off, +} + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + let led = Output::new(p.PIN_25, Level::Low); + + let mut mc = Multicore::new(); + let (_, core1) = mc.cores(); + let _ = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); + }); + + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +} + +#[embassy_executor::task] +async fn core0_task() { + info!("Hello from core 0"); + loop { + CHANNEL.send(LedState::On).await; + Timer::after(Duration::from_millis(100)).await; + CHANNEL.send(LedState::Off).await; + Timer::after(Duration::from_millis(400)).await; + } +} + +#[embassy_executor::task] +async fn core1_task(mut led: Output<'static, PIN_25>) { + info!("Hello from core 1"); + loop { + match CHANNEL.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} \ No newline at end of file From 34eaade14fbf521d2e0e37bdeb870787a184ee2e Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 08:33:09 +0100 Subject: [PATCH 203/374] fmt --- examples/rp/src/bin/multicore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 46d3cd17..cfb91e21 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -6,11 +6,11 @@ use defmt::*; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; +use embassy_rp::multicore::{Multicore, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Timer}; -use embassy_rp::multicore::{Multicore, Stack}; use {defmt_rtt as _, panic_probe as _}; static mut CORE1_STACK: Stack<4096> = Stack::new(); @@ -59,4 +59,4 @@ async fn core1_task(mut led: Output<'static, PIN_25>) { LedState::Off => led.set_low(), } } -} \ No newline at end of file +} From cc0248d83ad34941480cd71d0255054d5317ab74 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 12:42:08 +0100 Subject: [PATCH 204/374] Select critical-section in tests --- tests/rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index a07b479e..ffde4c7f 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } From d8821cfd41eb1776b904a5766be43f242af938f7 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 12:57:45 +0100 Subject: [PATCH 205/374] Feature gate critical-section-impl --- embassy-rp/Cargo.toml | 5 ++++- embassy-rp/src/lib.rs | 2 ++ embassy-rp/src/multicore.rs | 4 +++- examples/rp/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 07cd1bc1..694f0dc7 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -15,6 +15,9 @@ flavors = [ [features] defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] +# critical section that is safe for multicore use +critical-section-impl = ["critical-section/restore-state-u8"] + # 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. # If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. @@ -50,7 +53,7 @@ nb = "1.0.0" cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -critical-section = { version = "1.1", features = ["restore-state-u8"] } +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.4.0", features = ["async"], optional = true } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9a2fb7fc..2cd99f45 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -5,7 +5,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +#[cfg(feature = "critical-section-impl")] mod critical_section_impl; + mod intrinsics; pub mod adc; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index dd3b70a6..cc5c192b 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -4,6 +4,9 @@ //! It provides functionality for setting up the stack, and starting core1. //! //! The entrypoint for core1 can be any function that never returns, including closures. +//! +//! Enable the `critical-section-impl` feature in embassy-rp when sharing data across cores using +//! the `embassy-sync` primitives and `CriticalSectionRawMutex`. use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; @@ -57,7 +60,6 @@ fn install_stack_guard(stack_bottom: *mut usize) { #[inline(always)] fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); - // TODO: irq priorities } /// Multicore execution management. diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index bd624a32..34a2d36d 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.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", "time-driver"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", 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" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index ffde4c7f..572a9ce8 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.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", "time-driver"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" From 96d6c7243b7b5f7f8c90dab666ded0ca0cf29c75 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 13:43:29 +0100 Subject: [PATCH 206/374] Cleanup --- embassy-rp/src/multicore.rs | 12 ++++++------ examples/rp/src/bin/multicore.rs | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index cc5c192b..bf635db2 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -1,4 +1,4 @@ -//! Multicore support +//! MultiCore support //! //! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. //! It provides functionality for setting up the stack, and starting core1. @@ -62,9 +62,9 @@ fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); } -/// Multicore execution management. -pub struct Multicore { - cores: (Core, Core), +/// MultiCore execution management. +pub struct MultiCore { + pub cores: (Core, Core), } /// Data type for a properly aligned stack of N 32-bit (usize) words @@ -81,8 +81,8 @@ impl Stack { } } -impl Multicore { - /// Create a new |Multicore| instance. +impl MultiCore { + /// Create a new |MultiCore| instance. pub fn new() -> Self { Self { cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index cfb91e21..53941da6 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{Multicore, Stack}; +use embassy_rp::multicore::{MultiCore, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -28,9 +28,8 @@ fn main() -> ! { let p = embassy_rp::init(Default::default()); let led = Output::new(p.PIN_25, Level::Low); - let mut mc = Multicore::new(); - let (_, core1) = mc.cores(); - let _ = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + let mut mc = MultiCore::new(); + let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { let executor1 = EXECUTOR1.init(Executor::new()); executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); }); From eb1d2e1295cfd2f0580355d69c93387898eaabd4 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 04:02:28 +0100 Subject: [PATCH 207/374] Pause CORE1 execution during flash operations --- embassy-rp/src/flash.rs | 29 ++- embassy-rp/src/multicore.rs | 343 +++++++++++++++++++++--------------- 2 files changed, 223 insertions(+), 149 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index a972d5f6..f2137ebe 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -6,6 +6,7 @@ use embedded_storage::nor_flash::{ ReadNorFlash, }; +use crate::pac; use crate::peripherals::FLASH; pub const FLASH_BASE: usize = 0x10000000; @@ -28,6 +29,7 @@ pub enum Error { OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, + InvalidCore, Other, } @@ -46,7 +48,7 @@ impl NorFlashError for Error { match self { Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, Self::Unaligned => NorFlashErrorKind::NotAligned, - Self::Other => NorFlashErrorKind::Other, + _ => NorFlashErrorKind::Other, } } } @@ -87,7 +89,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let len = to - from; - unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; + unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true))? }; Ok(()) } @@ -112,7 +114,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let unaligned_offset = offset as usize - start; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } } let remaining_len = bytes.len() - start_padding; @@ -130,12 +132,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { if bytes.as_ptr() as usize >= 0x2000_0000 { let aligned_data = &bytes[start_padding..end_padding]; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true))? } } else { for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { let mut ram_buf = [0xFF_u8; PAGE_SIZE]; ram_buf.copy_from_slice(chunk); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true))? } aligned_offset += PAGE_SIZE; } } @@ -150,7 +152,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } } Ok(()) @@ -159,10 +161,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { /// Make sure to uphold the contract points with rp2040-flash. /// - interrupts must be disabled /// - DMA must not access flash memory - unsafe fn in_ram(&mut self, operation: impl FnOnce()) { + unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> { let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; - // TODO: Make sure CORE1 is paused during the entire duration of the RAM function + // Make sure we're running on CORE0 + let core_id: u32 = unsafe { pac::SIO.cpuid().read() }; + if core_id != 0 { + return Err(Error::InvalidCore); + } + + // Make sure CORE1 is paused during the entire duration of the RAM function + crate::multicore::pause_core1(); critical_section::with(|_| { // Pause all DMA channels for the duration of the ram operation @@ -185,6 +194,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } } }); + + // Resume CORE1 execution + crate::multicore::resume_core1(); + Ok(()) } } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index bf635db2..09d40b2e 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -11,14 +11,19 @@ use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; -use crate::pac; +use atomic_polyfill::AtomicBool; + +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::{interrupt, pac}; + +const PAUSE_TOKEN: u32 = 0xDEADBEEF; +const RESUME_TOKEN: u32 = !0xDEADBEEF; +static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); /// Errors for multicore operations. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// Operation is invalid on this core. - InvalidCore, /// Core was unresponsive to commands. Unresponsive, } @@ -64,7 +69,7 @@ fn core1_setup(stack_bottom: *mut usize) { /// MultiCore execution management. pub struct MultiCore { - pub cores: (Core, Core), + pub cores: (Core0, Core1), } /// Data type for a properly aligned stack of N 32-bit (usize) words @@ -85,169 +90,225 @@ impl MultiCore { /// Create a new |MultiCore| instance. pub fn new() -> Self { Self { - cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), + cores: (Core0 {}, Core1 {}), } } /// Get the available |Core| instances. - pub fn cores(&mut self) -> &mut (Core, Core) { + pub fn cores(&mut self) -> &mut (Core0, Core1) { &mut self.cores } } /// A handle for controlling a logical core. -pub struct Core { - pub id: CoreId, +pub struct Core0 {} +/// A handle for controlling a logical core. +pub struct Core1 {} + +#[interrupt] +#[link_section = ".data.ram_func"] +unsafe fn SIO_IRQ_PROC1() { + let sio = pac::SIO; + // Clear IRQ + sio.fifo().st().write(|w| w.set_wof(false)); + + while sio.fifo().st().read().vld() { + // Pause CORE1 execution and disable interrupts + if fifo_read_wfe() == PAUSE_TOKEN { + cortex_m::interrupt::disable(); + // Signal to CORE0 that execution is paused + fifo_write(PAUSE_TOKEN); + // Wait for `resume` signal from CORE0 + while fifo_read_wfe() != RESUME_TOKEN { + cortex_m::asm::nop(); + } + cortex_m::interrupt::enable(); + // Signal to CORE0 that execution is resumed + fifo_write(RESUME_TOKEN); + } + } } -impl Core { - /// Spawn a function on this core. +impl Core1 { + /// Spawn a function on this core pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> where F: FnOnce() -> bad::Never + Send + 'static, { - fn fifo_write(value: u32) { - unsafe { - let sio = pac::SIO; - // Wait for the FIFO to have some space - while !sio.fifo().st().read().rdy() { - cortex_m::asm::nop(); - } - // Signal that it's safe for core 0 to get rid of the original value now. - sio.fifo().wr().write_value(value); - } + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + let entry = unsafe { ManuallyDrop::take(entry) }; + // Signal that it's safe for core 0 to get rid of the original value now. + fifo_write(1); - // Fire off an event to the other core. - // This is required as the other core may be `wfe` (waiting for event) - cortex_m::asm::sev(); + IS_CORE1_INIT.store(true, Ordering::Release); + // Enable fifo interrupt on CORE1 for `pause` functionality. + let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; + irq.enable(); + + entry() } - fn fifo_read() -> u32 { - unsafe { - let sio = pac::SIO; - // Keep trying until FIFO has data - loop { - if sio.fifo().st().read().vld() { - return sio.fifo().rd().read(); - } else { - // We expect the sending core to `sev` on write. - cortex_m::asm::wfe(); - } + // Reset the core + unsafe { + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); + } + psm.frce_off().modify(|w| w.set_proc1(false)); + } + + // Set up the stack + let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } + + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + + let p = unsafe { cortex_m::Peripherals::steal() }; + let vector_table = p.SCB.vtor.read(); + + // After reset, core 1 is waiting to receive commands over FIFO. + // This is the sequence to have it jump to some code. + let cmd_seq = [ + 0, + 0, + 1, + vector_table as usize, + stack_ptr as usize, + core1_startup:: as usize, + ]; + + let mut seq = 0; + let mut fails = 0; + loop { + let cmd = cmd_seq[seq] as u32; + if cmd == 0 { + fifo_drain(); + cortex_m::asm::sev(); + } + fifo_write(cmd); + + let response = fifo_read(); + if cmd == response { + seq += 1; + } else { + seq = 0; + fails += 1; + if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint, + // so we have to drop it ourselves. + drop(ManuallyDrop::into_inner(entry)); + return Err(Error::Unresponsive); } } + if seq >= cmd_seq.len() { + break; + } } - fn fifo_drain() { - unsafe { - let sio = pac::SIO; - while sio.fifo().st().read().vld() { - let _ = sio.fifo().rd().read(); - } - } + // Wait until the other core has copied `entry` before returning. + fifo_read(); + + Ok(()) + } +} + +/// Pause execution on CORE1. +pub fn pause_core1() { + if IS_CORE1_INIT.load(Ordering::Acquire) { + fifo_write(PAUSE_TOKEN); + // Wait for CORE1 to signal it has paused execution. + while fifo_read() != PAUSE_TOKEN {} + } +} + +/// Resume CORE1 execution. +pub fn resume_core1() { + if IS_CORE1_INIT.load(Ordering::Acquire) { + fifo_write(RESUME_TOKEN); + // Wait for CORE1 to signal it has resumed execution. + while fifo_read() != RESUME_TOKEN {} + } +} + +// Push a value to the inter-core FIFO, block until space is available +#[inline(always)] +fn fifo_write(value: u32) { + unsafe { + let sio = pac::SIO; + // Wait for the FIFO to have enough space + while !sio.fifo().st().read().rdy() { + cortex_m::asm::nop(); } + sio.fifo().wr().write_value(value); + } + // Fire off an event to the other core. + // This is required as the other core may be `wfe` (waiting for event) + cortex_m::asm::sev(); +} - match self.id { - CoreId::Core1 => { - // The first two ignored `u64` parameters are there to take up all of the registers, - // which means that the rest of the arguments are taken from the stack, - // where we're able to put them from core 0. - extern "C" fn core1_startup bad::Never>( - _: u64, - _: u64, - entry: &mut ManuallyDrop, - stack_bottom: *mut usize, - ) -> ! { - core1_setup(stack_bottom); - let entry = unsafe { ManuallyDrop::take(entry) }; - // Signal that it's safe for core 0 to get rid of the original value now. - fifo_write(1); - entry() - } +// Pop a value from inter-core FIFO, block until available +#[inline(always)] +fn fifo_read() -> u32 { + unsafe { + let sio = pac::SIO; + // Wait until FIFO has data + while !sio.fifo().st().read().vld() { + cortex_m::asm::nop(); + } + sio.fifo().rd().read() + } +} - // Reset the core - unsafe { - let psm = pac::PSM; - psm.frce_off().modify(|w| w.set_proc1(true)); - while !psm.frce_off().read().proc1() { - cortex_m::asm::nop(); - } - psm.frce_off().modify(|w| w.set_proc1(false)); - } +// Pop a value from inter-core FIFO, `wfe` until available +#[inline(always)] +fn fifo_read_wfe() -> u32 { + unsafe { + let sio = pac::SIO; + // Wait until FIFO has data + while !sio.fifo().st().read().vld() { + cortex_m::asm::wfe(); + } + sio.fifo().rd().read() + } +} - // Set up the stack - let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; - - // We don't want to drop this, since it's getting moved to the other core. - let mut entry = ManuallyDrop::new(entry); - - // Push the arguments to `core1_startup` onto the stack. - unsafe { - // Push `stack_bottom`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); - - // Push `entry`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); - } - - // Make sure the compiler does not reorder the stack writes after to after the - // below FIFO writes, which would result in them not being seen by the second - // core. - // - // From the compiler perspective, this doesn't guarantee that the second core - // actually sees those writes. However, we know that the RP2040 doesn't have - // memory caches, and writes happen in-order. - compiler_fence(Ordering::Release); - - let p = unsafe { cortex_m::Peripherals::steal() }; - let vector_table = p.SCB.vtor.read(); - - // After reset, core 1 is waiting to receive commands over FIFO. - // This is the sequence to have it jump to some code. - let cmd_seq = [ - 0, - 0, - 1, - vector_table as usize, - stack_ptr as usize, - core1_startup:: as usize, - ]; - - let mut seq = 0; - let mut fails = 0; - loop { - let cmd = cmd_seq[seq] as u32; - if cmd == 0 { - fifo_drain(); - cortex_m::asm::sev(); - } - fifo_write(cmd); - - let response = fifo_read(); - if cmd == response { - seq += 1; - } else { - seq = 0; - fails += 1; - if fails > 16 { - // The second core isn't responding, and isn't going to take the entrypoint, - // so we have to drop it ourselves. - drop(ManuallyDrop::into_inner(entry)); - return Err(Error::Unresponsive); - } - } - if seq >= cmd_seq.len() { - break; - } - } - - // Wait until the other core has copied `entry` before returning. - fifo_read(); - - Ok(()) - } - _ => Err(Error::InvalidCore), +// Drain inter-core FIFO +#[inline(always)] +fn fifo_drain() { + unsafe { + let sio = pac::SIO; + while sio.fifo().st().read().vld() { + let _ = sio.fifo().rd().read(); } } } From 95fdc7c55237d749f11d94f1e3f7602f3c057483 Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 13 Dec 2022 14:06:37 +0800 Subject: [PATCH 208/374] fix net README.md --- embassy-net/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/README.md b/embassy-net/README.md index 9657e358..470926c5 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -17,11 +17,13 @@ sudo ip -6 route add fe80::/64 dev tap0 sudo ip -6 route add fdaa::/64 dev tap0 ``` +Second, have something listening there. For example `nc -l 8000` + Then run the example located in the `examples` folder: ```sh cd $EMBASSY_ROOT/examples/std/ -cargo run --bin net +cargo run --bin net -- --static-ip ``` ## License From aea28c8aa0bc13251835c6e38f4e1fbcbd30f4db Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 09:45:11 +0100 Subject: [PATCH 209/374] Add usage in to docs --- embassy-rp/src/multicore.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 09d40b2e..3703fe81 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -1,4 +1,4 @@ -//! MultiCore support +//! Multicore support //! //! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. //! It provides functionality for setting up the stack, and starting core1. @@ -7,6 +7,28 @@ //! //! Enable the `critical-section-impl` feature in embassy-rp when sharing data across cores using //! the `embassy-sync` primitives and `CriticalSectionRawMutex`. +//! +//! # Usage +//! ```no_run +//! static mut CORE1_STACK: Stack<4096> = Stack::new(); +//! static EXECUTOR0: StaticCell = StaticCell::new(); +//! static EXECUTOR1: StaticCell = StaticCell::new(); +//! +//! #[cortex_m_rt::entry] +//! fn main() -> ! { +//! let p = embassy_rp::init(Default::default()); +//! +//! let mut mc = MultiCore::new(); +//! let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { +//! let executor1 = EXECUTOR1.init(Executor::new()); +//! executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); +//! }); +//! +//! let executor0 = EXECUTOR0.init(Executor::new()); +//! executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +//! } +//! ``` +//! use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; @@ -28,14 +50,6 @@ pub enum Error { Unresponsive, } -/// Core ID -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CoreId { - Core0, - Core1, -} - #[inline(always)] fn install_stack_guard(stack_bottom: *mut usize) { let core = unsafe { cortex_m::Peripherals::steal() }; From 13d9d8fde109c09e310cbf1735917a768f4a1cf6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 13:49:51 +0100 Subject: [PATCH 210/374] Refactor after review --- embassy-rp/src/lib.rs | 2 + embassy-rp/src/multicore.rs | 251 +++++++++++++------------------ examples/rp/src/bin/multicore.rs | 5 +- tests/rp/src/bin/multicore.rs | 47 ++++++ 4 files changed, 159 insertions(+), 146 deletions(-) create mode 100644 tests/rp/src/bin/multicore.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 2cd99f45..c6442c56 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -106,6 +106,8 @@ embassy_hal_common::peripherals! { FLASH, ADC, + + CORE1, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 3703fe81..2dfa215b 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -36,20 +36,13 @@ use core::sync::atomic::{compiler_fence, Ordering}; use atomic_polyfill::AtomicBool; use crate::interrupt::{Interrupt, InterruptExt}; +use crate::peripherals::CORE1; use crate::{interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); -/// Errors for multicore operations. -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// Core was unresponsive to commands. - Unresponsive, -} - #[inline(always)] fn install_stack_guard(stack_bottom: *mut usize) { let core = unsafe { cortex_m::Peripherals::steal() }; @@ -81,44 +74,20 @@ fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); } -/// MultiCore execution management. -pub struct MultiCore { - pub cores: (Core0, Core1), -} - -/// Data type for a properly aligned stack of N 32-bit (usize) words +/// Data type for a properly aligned stack of N bytes #[repr(C, align(32))] pub struct Stack { /// Memory to be used for the stack - pub mem: [usize; SIZE], + pub mem: [u8; SIZE], } impl Stack { /// Construct a stack of length SIZE, initialized to 0 pub const fn new() -> Stack { - Stack { mem: [0; SIZE] } + Stack { mem: [0_u8; SIZE] } } } -impl MultiCore { - /// Create a new |MultiCore| instance. - pub fn new() -> Self { - Self { - cores: (Core0 {}, Core1 {}), - } - } - - /// Get the available |Core| instances. - pub fn cores(&mut self) -> &mut (Core0, Core1) { - &mut self.cores - } -} - -/// A handle for controlling a logical core. -pub struct Core0 {} -/// A handle for controlling a logical core. -pub struct Core1 {} - #[interrupt] #[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_PROC1() { @@ -143,117 +112,113 @@ unsafe fn SIO_IRQ_PROC1() { } } -impl Core1 { - /// Spawn a function on this core - pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> - where - F: FnOnce() -> bad::Never + Send + 'static, - { - // The first two ignored `u64` parameters are there to take up all of the registers, - // which means that the rest of the arguments are taken from the stack, - // where we're able to put them from core 0. - extern "C" fn core1_startup bad::Never>( - _: u64, - _: u64, - entry: &mut ManuallyDrop, - stack_bottom: *mut usize, - ) -> ! { - core1_setup(stack_bottom); - let entry = unsafe { ManuallyDrop::take(entry) }; - // Signal that it's safe for core 0 to get rid of the original value now. - fifo_write(1); +/// Spawn a function on this core +pub fn spawn_core1(_core1: CORE1, stack: &'static mut Stack, entry: F) +where + F: FnOnce() -> bad::Never + Send + 'static, +{ + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + let entry = unsafe { ManuallyDrop::take(entry) }; + // Signal that it's safe for core 0 to get rid of the original value now. + fifo_write(1); - IS_CORE1_INIT.store(true, Ordering::Release); - // Enable fifo interrupt on CORE1 for `pause` functionality. - let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; - irq.enable(); + IS_CORE1_INIT.store(true, Ordering::Release); + // Enable fifo interrupt on CORE1 for `pause` functionality. + let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; + irq.enable(); - entry() - } - - // Reset the core - unsafe { - let psm = pac::PSM; - psm.frce_off().modify(|w| w.set_proc1(true)); - while !psm.frce_off().read().proc1() { - cortex_m::asm::nop(); - } - psm.frce_off().modify(|w| w.set_proc1(false)); - } - - // Set up the stack - let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; - - // We don't want to drop this, since it's getting moved to the other core. - let mut entry = ManuallyDrop::new(entry); - - // Push the arguments to `core1_startup` onto the stack. - unsafe { - // Push `stack_bottom`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); - - // Push `entry`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); - } - - // Make sure the compiler does not reorder the stack writes after to after the - // below FIFO writes, which would result in them not being seen by the second - // core. - // - // From the compiler perspective, this doesn't guarantee that the second core - // actually sees those writes. However, we know that the RP2040 doesn't have - // memory caches, and writes happen in-order. - compiler_fence(Ordering::Release); - - let p = unsafe { cortex_m::Peripherals::steal() }; - let vector_table = p.SCB.vtor.read(); - - // After reset, core 1 is waiting to receive commands over FIFO. - // This is the sequence to have it jump to some code. - let cmd_seq = [ - 0, - 0, - 1, - vector_table as usize, - stack_ptr as usize, - core1_startup:: as usize, - ]; - - let mut seq = 0; - let mut fails = 0; - loop { - let cmd = cmd_seq[seq] as u32; - if cmd == 0 { - fifo_drain(); - cortex_m::asm::sev(); - } - fifo_write(cmd); - - let response = fifo_read(); - if cmd == response { - seq += 1; - } else { - seq = 0; - fails += 1; - if fails > 16 { - // The second core isn't responding, and isn't going to take the entrypoint, - // so we have to drop it ourselves. - drop(ManuallyDrop::into_inner(entry)); - return Err(Error::Unresponsive); - } - } - if seq >= cmd_seq.len() { - break; - } - } - - // Wait until the other core has copied `entry` before returning. - fifo_read(); - - Ok(()) + entry() } + + // Reset the core + unsafe { + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); + } + psm.frce_off().modify(|w| w.set_proc1(false)); + } + + let mem = unsafe { core::slice::from_raw_parts_mut(stack.mem.as_mut_ptr() as *mut usize, stack.mem.len() / 4) }; + + // Set up the stack + let mut stack_ptr = unsafe { mem.as_mut_ptr().add(mem.len()) }; + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(mem.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } + + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + + let p = unsafe { cortex_m::Peripherals::steal() }; + let vector_table = p.SCB.vtor.read(); + + // After reset, core 1 is waiting to receive commands over FIFO. + // This is the sequence to have it jump to some code. + let cmd_seq = [ + 0, + 0, + 1, + vector_table as usize, + stack_ptr as usize, + core1_startup:: as usize, + ]; + + let mut seq = 0; + let mut fails = 0; + loop { + let cmd = cmd_seq[seq] as u32; + if cmd == 0 { + fifo_drain(); + cortex_m::asm::sev(); + } + fifo_write(cmd); + + let response = fifo_read(); + if cmd == response { + seq += 1; + } else { + seq = 0; + fails += 1; + if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint + panic!("CORE1 not responding"); + } + } + if seq >= cmd_seq.len() { + break; + } + } + + // Wait until the other core has copied `entry` before returning. + fifo_read(); } /// Pause execution on CORE1. diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 53941da6..376b2b61 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{MultiCore, Stack}; +use embassy_rp::multicore::{spawn_core1, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -28,8 +28,7 @@ fn main() -> ! { let p = embassy_rp::init(Default::default()); let led = Output::new(p.PIN_25, Level::Low); - let mut mc = MultiCore::new(); - let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { let executor1 = EXECUTOR1.init(Executor::new()); executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); }); diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs new file mode 100644 index 00000000..da78e887 --- /dev/null +++ b/tests/rp/src/bin/multicore.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<1024> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL0: Channel = Channel::new(); +static CHANNEL1: Channel = Channel::new(); + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); + }); + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +} + +#[embassy_executor::task] +async fn core0_task() { + info!("CORE0 is running"); + let ping = true; + CHANNEL0.send(ping).await; + let pong = CHANNEL1.recv().await; + assert_eq!(ping, pong); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[embassy_executor::task] +async fn core1_task() { + info!("CORE1 is running"); + let ping = CHANNEL0.recv().await; + CHANNEL1.send(ping).await; +} From 731eb3c6e31ca7218636c1fba266b2c643e8fb44 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 13:55:23 +0100 Subject: [PATCH 211/374] fmt --- embassy-rp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 248884e7..0ea97aec 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -116,7 +116,7 @@ embassy_hal_common::peripherals! { ADC, CORE1, - + PIO0, PIO1, } From c4d8f3579e4927446421730d52a409d724f2800c Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 14:15:04 +0100 Subject: [PATCH 212/374] Update usage in docs --- embassy-rp/src/multicore.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 2dfa215b..8290c62a 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -18,8 +18,7 @@ //! fn main() -> ! { //! let p = embassy_rp::init(Default::default()); //! -//! let mut mc = MultiCore::new(); -//! let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { +//! spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { //! let executor1 = EXECUTOR1.init(Executor::new()); //! executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); //! }); @@ -28,7 +27,6 @@ //! executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); //! } //! ``` -//! use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; From ac74613b5a7be72acd8d5259055963f8b4aba7fd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 15:55:46 +0100 Subject: [PATCH 213/374] net: remove packet pool. The pool was prone to deadlocks, especially due to having a single pool for rx+tx. If the pool got full with rx'd packets it would deadlock because processing a rx packet requires doing another allocation on the pool, for the possibly tx'd response, before deallocating the rx'd packet. This also allows Device impls to allocate the packet memory in a particular RAM kind, if needed for example to do DMA. The `Device` trait is now token-based, like smoltcp's. In the end, this is better because it allows callers to manage memory however they want (including with a pool if they want to). --- embassy-net/src/device.rs | 163 ++++++++++++++++++--------------- embassy-net/src/lib.rs | 6 +- embassy-net/src/packet_pool.rs | 107 ---------------------- embassy-net/src/stack.rs | 24 +++-- embassy-net/src/tcp.rs | 2 +- 5 files changed, 105 insertions(+), 197 deletions(-) delete mode 100644 embassy-net/src/packet_pool.rs diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 4bdfd772..5d86ca91 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -1,10 +1,7 @@ -use core::task::Waker; +use core::task::Context; -use smoltcp::phy::{Device as SmolDevice, DeviceCapabilities}; -use smoltcp::time::Instant as SmolInstant; - -use crate::packet_pool::PacketBoxExt; -use crate::{Packet, PacketBox, PacketBuf}; +use smoltcp::phy; +pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium}; #[derive(PartialEq, Eq, Clone, Copy)] pub enum LinkState { @@ -13,115 +10,133 @@ pub enum LinkState { } pub trait Device { - fn is_transmit_ready(&mut self) -> bool; - fn transmit(&mut self, pkt: PacketBuf); - fn receive(&mut self) -> Option; + type RxToken<'a>: RxToken + where + Self: 'a; + type TxToken<'a>: TxToken + where + Self: 'a; - fn register_waker(&mut self, waker: &Waker); - fn capabilities(&self) -> DeviceCapabilities; - fn link_state(&mut self) -> LinkState; + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + fn transmit(&mut self, cx: &mut Context) -> Option>; + fn link_state(&mut self, cx: &mut Context) -> LinkState; + + fn capabilities(&self) -> phy::DeviceCapabilities; fn ethernet_address(&self) -> [u8; 6]; } impl Device for &mut T { - fn is_transmit_ready(&mut self) -> bool { - T::is_transmit_ready(self) + type RxToken<'a> = T::RxToken<'a> + where + Self: 'a; + type TxToken<'a> = T::TxToken<'a> + where + Self: 'a; + + fn transmit(&mut self, cx: &mut Context) -> Option> { + T::transmit(self, cx) } - fn transmit(&mut self, pkt: PacketBuf) { - T::transmit(self, pkt) + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + T::receive(self, cx) } - fn receive(&mut self) -> Option { - T::receive(self) - } - fn register_waker(&mut self, waker: &Waker) { - T::register_waker(self, waker) - } - fn capabilities(&self) -> DeviceCapabilities { + fn capabilities(&self) -> phy::DeviceCapabilities { T::capabilities(self) } - fn link_state(&mut self) -> LinkState { - T::link_state(self) + fn link_state(&mut self, cx: &mut Context) -> LinkState { + T::link_state(self, cx) } fn ethernet_address(&self) -> [u8; 6] { T::ethernet_address(self) } } -pub struct DeviceAdapter { - pub device: D, - caps: DeviceCapabilities, +/// A token to receive a single network packet. +pub trait RxToken { + /// Consumes the token to receive a single network packet. + /// + /// This method receives a packet and then calls the given closure `f` with the raw + /// packet bytes as argument. + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; } -impl DeviceAdapter { - pub(crate) fn new(device: D) -> Self { - Self { - caps: device.capabilities(), - device, - } - } +/// A token to transmit a single network packet. +pub trait TxToken { + /// Consumes the token to send a single network packet. + /// + /// This method constructs a transmit buffer of size `len` and calls the passed + /// closure `f` with a mutable reference to that buffer. The closure should construct + /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure + /// returns, the transmit buffer is sent out. + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; } -impl SmolDevice for DeviceAdapter { - type RxToken<'a> = RxToken where Self: 'a; - type TxToken<'a> = TxToken<'a, D> where Self: 'a; +/////////////////////////// + +pub(crate) struct DeviceAdapter<'d, 'c, T> +where + T: Device, +{ + // must be Some when actually using this to rx/tx + pub cx: Option<&'d mut Context<'c>>, + pub inner: &'d mut T, +} + +impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> +where + T: Device, +{ + type RxToken<'a> = RxTokenAdapter> where Self: 'a; + type TxToken<'a> = TxTokenAdapter> where Self: 'a; fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - let tx_pkt = PacketBox::new(Packet::new())?; - let rx_pkt = self.device.receive()?; - let rx_token = RxToken { pkt: rx_pkt }; - let tx_token = TxToken { - device: &mut self.device, - pkt: tx_pkt, - }; - - Some((rx_token, tx_token)) + self.inner + .receive(self.cx.as_deref_mut().unwrap()) + .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) } /// Construct a transmit token. fn transmit(&mut self) -> Option> { - if !self.device.is_transmit_ready() { - return None; - } - - let tx_pkt = PacketBox::new(Packet::new())?; - Some(TxToken { - device: &mut self.device, - pkt: tx_pkt, - }) + self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) } /// Get a description of device capabilities. - fn capabilities(&self) -> DeviceCapabilities { - self.caps.clone() + fn capabilities(&self) -> phy::DeviceCapabilities { + self.inner.capabilities() } } -pub struct RxToken { - pkt: PacketBuf, -} +pub(crate) struct RxTokenAdapter(T) +where + T: RxToken; -impl smoltcp::phy::RxToken for RxToken { - fn consume(mut self, _timestamp: SmolInstant, f: F) -> smoltcp::Result +impl phy::RxToken for RxTokenAdapter +where + T: RxToken, +{ + fn consume(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result where F: FnOnce(&mut [u8]) -> smoltcp::Result, { - f(&mut self.pkt) + self.0.consume(|buf| f(buf)) } } -pub struct TxToken<'a, D: Device> { - device: &'a mut D, - pkt: PacketBox, -} +pub(crate) struct TxTokenAdapter(T) +where + T: TxToken; -impl<'a, D: Device> smoltcp::phy::TxToken for TxToken<'a, D> { - fn consume(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result +impl phy::TxToken for TxTokenAdapter +where + T: TxToken, +{ + fn consume(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result where F: FnOnce(&mut [u8]) -> smoltcp::Result, { - let mut buf = self.pkt.slice(0..len); - let r = f(&mut buf)?; - self.device.transmit(buf); - Ok(r) + self.0.consume(len, |buf| f(buf)) } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index edb96984..e18e819c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -8,12 +8,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -mod device; -mod packet_pool; +pub mod device; mod stack; -pub use device::{Device, LinkState}; -pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU}; pub use stack::{Config, ConfigStrategy, Stack, StackResources}; #[cfg(feature = "tcp")] @@ -23,7 +20,6 @@ pub mod tcp; pub mod udp; // smoltcp reexports -pub use smoltcp::phy::{DeviceCapabilities, Medium}; pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs deleted file mode 100644 index cb8a1316..00000000 --- a/embassy-net/src/packet_pool.rs +++ /dev/null @@ -1,107 +0,0 @@ -use core::ops::{Deref, DerefMut, Range}; - -use as_slice::{AsMutSlice, AsSlice}; -use atomic_pool::{pool, Box}; - -pub const MTU: usize = 1516; - -#[cfg(feature = "pool-4")] -pub const PACKET_POOL_SIZE: usize = 4; - -#[cfg(feature = "pool-8")] -pub const PACKET_POOL_SIZE: usize = 8; - -#[cfg(feature = "pool-16")] -pub const PACKET_POOL_SIZE: usize = 16; - -#[cfg(feature = "pool-32")] -pub const PACKET_POOL_SIZE: usize = 32; - -#[cfg(feature = "pool-64")] -pub const PACKET_POOL_SIZE: usize = 64; - -#[cfg(feature = "pool-128")] -pub const PACKET_POOL_SIZE: usize = 128; - -pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); -pub type PacketBox = Box; - -#[repr(align(4))] -pub struct Packet(pub [u8; MTU]); - -impl Packet { - pub const fn new() -> Self { - Self([0; MTU]) - } -} - -pub trait PacketBoxExt { - fn slice(self, range: Range) -> PacketBuf; -} - -impl PacketBoxExt for PacketBox { - fn slice(self, range: Range) -> PacketBuf { - PacketBuf { packet: self, range } - } -} - -impl AsSlice for Packet { - type Element = u8; - - fn as_slice(&self) -> &[Self::Element] { - &self.deref()[..] - } -} - -impl AsMutSlice for Packet { - fn as_mut_slice(&mut self) -> &mut [Self::Element] { - &mut self.deref_mut()[..] - } -} - -impl Deref for Packet { - type Target = [u8; MTU]; - - fn deref(&self) -> &[u8; MTU] { - &self.0 - } -} - -impl DerefMut for Packet { - fn deref_mut(&mut self) -> &mut [u8; MTU] { - &mut self.0 - } -} - -pub struct PacketBuf { - packet: PacketBox, - range: Range, -} - -impl AsSlice for PacketBuf { - type Element = u8; - - fn as_slice(&self) -> &[Self::Element] { - &self.packet[self.range.clone()] - } -} - -impl AsMutSlice for PacketBuf { - fn as_mut_slice(&mut self) -> &mut [Self::Element] { - &mut self.packet[self.range.clone()] - } -} - -impl Deref for PacketBuf { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - &self.packet[self.range.clone()] - } -} - -impl DerefMut for PacketBuf { - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.packet[self.range.clone()] - } -} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 5c4fb044..21316e48 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -12,7 +12,7 @@ use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "medium-ethernet")] use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; #[cfg(feature = "medium-ethernet")] -use smoltcp::phy::{Device as _, Medium}; +use smoltcp::phy::Medium; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; use smoltcp::time::Instant as SmolInstant; @@ -67,7 +67,7 @@ pub struct Stack { } struct Inner { - device: DeviceAdapter, + device: D, link_up: bool, config: Option, #[cfg(feature = "dhcpv4")] @@ -83,7 +83,7 @@ pub(crate) struct SocketStack { impl Stack { pub fn new( - device: D, + mut device: D, config: ConfigStrategy, resources: &'static mut StackResources, random_seed: u64, @@ -98,8 +98,6 @@ impl Stack { [0, 0, 0, 0, 0, 0] }; - let mut device = DeviceAdapter::new(device); - let mut b = InterfaceBuilder::new(); b = b.ip_addrs(&mut resources.addresses[..]); b = b.random_seed(random_seed); @@ -111,7 +109,10 @@ impl Stack { b = b.routes(Routes::new(&mut resources.routes[..])); } - let iface = b.finalize(&mut device); + let iface = b.finalize(&mut DeviceAdapter { + inner: &mut device, + cx: None, + }); let sockets = SocketSet::new(&mut resources.sockets[..]); @@ -155,7 +156,7 @@ impl Stack { } pub fn ethernet_address(&self) -> [u8; 6] { - self.with(|_s, i| i.device.device.ethernet_address()) + self.with(|_s, i| i.device.ethernet_address()) } pub fn is_link_up(&self) -> bool { @@ -238,11 +239,14 @@ impl Inner { } fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { - self.device.device.register_waker(cx.waker()); s.waker.register(cx.waker()); let timestamp = instant_to_smoltcp(Instant::now()); - if s.iface.poll(timestamp, &mut self.device, &mut s.sockets).is_err() { + let mut smoldev = DeviceAdapter { + cx: Some(cx), + inner: &mut self.device, + }; + if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { // If poll() returns error, it may not be done yet, so poll again later. cx.waker().wake_by_ref(); return; @@ -250,7 +254,7 @@ impl Inner { // Update link up let old_link_up = self.link_up; - self.link_up = self.device.device.link_state() == LinkState::Up; + self.link_up = self.device.link_state(cx) == LinkState::Up; // Print when changed if old_link_up != self.link_up { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 73cf2d4e..0ed4b81e 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -9,8 +9,8 @@ use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use super::stack::Stack; +use crate::device::Device; use crate::stack::SocketStack; -use crate::Device; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From aaaf5f23a8a3a0df1ad2186802e2afc061a74b72 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 16:02:28 +0100 Subject: [PATCH 214/374] net: move stack into lib.rs --- embassy-net/src/lib.rs | 307 ++++++++++++++++++++++++++++++++++++++- embassy-net/src/stack.rs | 306 -------------------------------------- embassy-net/src/tcp.rs | 3 +- embassy-net/src/udp.rs | 3 +- 4 files changed, 304 insertions(+), 315 deletions(-) delete mode 100644 embassy-net/src/stack.rs diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e18e819c..afe0d6da 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -9,16 +9,28 @@ pub(crate) mod fmt; pub mod device; -mod stack; - -pub use stack::{Config, ConfigStrategy, Stack, StackResources}; - #[cfg(feature = "tcp")] pub mod tcp; - #[cfg(feature = "udp")] pub mod udp; +use core::cell::RefCell; +use core::future::{poll_fn, Future}; +use core::task::{Context, Poll}; + +use embassy_sync::waitqueue::WakerRegistration; +use embassy_time::{Instant, Timer}; +use futures::pin_mut; +use heapless::Vec; +#[cfg(feature = "dhcpv4")] +use smoltcp::iface::SocketHandle; +use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; +#[cfg(feature = "medium-ethernet")] +use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; +#[cfg(feature = "medium-ethernet")] +use smoltcp::phy::Medium; +#[cfg(feature = "dhcpv4")] +use smoltcp::socket::dhcpv4; // smoltcp reexports pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] @@ -28,3 +40,288 @@ pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; #[cfg(feature = "udp")] pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; + +use crate::device::{Device, DeviceAdapter, LinkState}; + +const LOCAL_PORT_MIN: u16 = 1025; +const LOCAL_PORT_MAX: u16 = 65535; + +pub struct StackResources { + addresses: [IpCidr; ADDR], + sockets: [SocketStorage<'static>; SOCK], + + #[cfg(feature = "medium-ethernet")] + routes: [Option<(IpCidr, Route)>; 1], + #[cfg(feature = "medium-ethernet")] + neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], +} + +impl StackResources { + pub fn new() -> Self { + Self { + addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], + sockets: [SocketStorage::EMPTY; SOCK], + #[cfg(feature = "medium-ethernet")] + routes: [None; 1], + #[cfg(feature = "medium-ethernet")] + neighbor_cache: [None; NEIGHBOR], + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config { + pub address: Ipv4Cidr, + pub gateway: Option, + pub dns_servers: Vec, +} + +pub enum ConfigStrategy { + Static(Config), + #[cfg(feature = "dhcpv4")] + Dhcp, +} + +pub struct Stack { + pub(crate) socket: RefCell, + inner: RefCell>, +} + +struct Inner { + device: D, + link_up: bool, + config: Option, + #[cfg(feature = "dhcpv4")] + dhcp_socket: Option, +} + +pub(crate) struct SocketStack { + pub(crate) sockets: SocketSet<'static>, + pub(crate) iface: Interface<'static>, + pub(crate) waker: WakerRegistration, + next_local_port: u16, +} + +impl Stack { + pub fn new( + mut device: D, + config: ConfigStrategy, + resources: &'static mut StackResources, + random_seed: u64, + ) -> Self { + #[cfg(feature = "medium-ethernet")] + let medium = device.capabilities().medium; + + #[cfg(feature = "medium-ethernet")] + let ethernet_addr = if medium == Medium::Ethernet { + device.ethernet_address() + } else { + [0, 0, 0, 0, 0, 0] + }; + + let mut b = InterfaceBuilder::new(); + b = b.ip_addrs(&mut resources.addresses[..]); + b = b.random_seed(random_seed); + + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); + b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); + b = b.routes(Routes::new(&mut resources.routes[..])); + } + + let iface = b.finalize(&mut DeviceAdapter { + inner: &mut device, + cx: None, + }); + + let sockets = SocketSet::new(&mut resources.sockets[..]); + + let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; + + let mut inner = Inner { + device, + link_up: false, + config: None, + #[cfg(feature = "dhcpv4")] + dhcp_socket: None, + }; + let mut socket = SocketStack { + sockets, + iface, + waker: WakerRegistration::new(), + next_local_port, + }; + + match config { + ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), + #[cfg(feature = "dhcpv4")] + ConfigStrategy::Dhcp => { + let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); + inner.dhcp_socket = Some(handle); + } + } + + Self { + socket: RefCell::new(socket), + inner: RefCell::new(inner), + } + } + + fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { + f(&*self.socket.borrow(), &*self.inner.borrow()) + } + + fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { + f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) + } + + pub fn ethernet_address(&self) -> [u8; 6] { + self.with(|_s, i| i.device.ethernet_address()) + } + + pub fn is_link_up(&self) -> bool { + self.with(|_s, i| i.link_up) + } + + pub fn is_config_up(&self) -> bool { + self.with(|_s, i| i.config.is_some()) + } + + pub fn config(&self) -> Option { + self.with(|_s, i| i.config.clone()) + } + + pub async fn run(&self) -> ! { + poll_fn(|cx| { + self.with_mut(|s, i| i.poll(cx, s)); + Poll::<()>::Pending + }) + .await; + unreachable!() + } +} + +impl SocketStack { + #[allow(clippy::absurd_extreme_comparisons)] + pub fn get_local_port(&mut self) -> u16 { + let res = self.next_local_port; + self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; + res + } +} + +impl Inner { + fn apply_config(&mut self, s: &mut SocketStack, config: Config) { + #[cfg(feature = "medium-ethernet")] + let medium = self.device.capabilities().medium; + + debug!("Acquired IP configuration:"); + + debug!(" IP address: {}", config.address); + self.set_ipv4_addr(s, config.address); + + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + if let Some(gateway) = config.gateway { + debug!(" Default gateway: {}", gateway); + s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); + } else { + debug!(" Default gateway: None"); + s.iface.routes_mut().remove_default_ipv4_route(); + } + } + for (i, s) in config.dns_servers.iter().enumerate() { + debug!(" DNS server {}: {}", i, s); + } + + self.config = Some(config) + } + + #[allow(unused)] // used only with dhcp + fn unapply_config(&mut self, s: &mut SocketStack) { + #[cfg(feature = "medium-ethernet")] + let medium = self.device.capabilities().medium; + + debug!("Lost IP configuration"); + self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + s.iface.routes_mut().remove_default_ipv4_route(); + } + self.config = None + } + + fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { + s.iface.update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next().unwrap(); + *dest = IpCidr::Ipv4(cidr); + }); + } + + fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { + s.waker.register(cx.waker()); + + let timestamp = instant_to_smoltcp(Instant::now()); + let mut smoldev = DeviceAdapter { + cx: Some(cx), + inner: &mut self.device, + }; + if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { + // If poll() returns error, it may not be done yet, so poll again later. + cx.waker().wake_by_ref(); + return; + } + + // Update link up + let old_link_up = self.link_up; + self.link_up = self.device.link_state(cx) == LinkState::Up; + + // Print when changed + if old_link_up != self.link_up { + info!("link_up = {:?}", self.link_up); + } + + #[cfg(feature = "dhcpv4")] + if let Some(dhcp_handle) = self.dhcp_socket { + let socket = s.sockets.get_mut::(dhcp_handle); + + if self.link_up { + match socket.poll() { + None => {} + Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), + Some(dhcpv4::Event::Configured(config)) => { + let config = Config { + address: config.address, + gateway: config.router, + dns_servers: config.dns_servers, + }; + self.apply_config(s, config) + } + } + } else if old_link_up { + socket.reset(); + self.unapply_config(s); + } + } + //if old_link_up || self.link_up { + // self.poll_configurator(timestamp) + //} + + if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { + let t = Timer::at(instant_from_smoltcp(poll_at)); + pin_mut!(t); + if t.poll(cx).is_ready() { + cx.waker().wake_by_ref(); + } + } + } +} + +fn instant_to_smoltcp(instant: Instant) -> SmolInstant { + SmolInstant::from_millis(instant.as_millis() as i64) +} + +fn instant_from_smoltcp(instant: SmolInstant) -> Instant { + Instant::from_millis(instant.total_millis() as u64) +} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs deleted file mode 100644 index 21316e48..00000000 --- a/embassy-net/src/stack.rs +++ /dev/null @@ -1,306 +0,0 @@ -use core::cell::RefCell; -use core::future::{poll_fn, Future}; -use core::task::{Context, Poll}; - -use embassy_sync::waitqueue::WakerRegistration; -use embassy_time::{Instant, Timer}; -use futures::pin_mut; -use heapless::Vec; -#[cfg(feature = "dhcpv4")] -use smoltcp::iface::SocketHandle; -use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::phy::Medium; -#[cfg(feature = "dhcpv4")] -use smoltcp::socket::dhcpv4; -use smoltcp::time::Instant as SmolInstant; -#[cfg(feature = "medium-ethernet")] -use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress}; -use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr}; - -use crate::device::{Device, DeviceAdapter, LinkState}; - -const LOCAL_PORT_MIN: u16 = 1025; -const LOCAL_PORT_MAX: u16 = 65535; - -pub struct StackResources { - addresses: [IpCidr; ADDR], - sockets: [SocketStorage<'static>; SOCK], - - #[cfg(feature = "medium-ethernet")] - routes: [Option<(IpCidr, Route)>; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], -} - -impl StackResources { - pub fn new() -> Self { - Self { - addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], - sockets: [SocketStorage::EMPTY; SOCK], - #[cfg(feature = "medium-ethernet")] - routes: [None; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [None; NEIGHBOR], - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config { - pub address: Ipv4Cidr, - pub gateway: Option, - pub dns_servers: Vec, -} - -pub enum ConfigStrategy { - Static(Config), - #[cfg(feature = "dhcpv4")] - Dhcp, -} - -pub struct Stack { - pub(crate) socket: RefCell, - inner: RefCell>, -} - -struct Inner { - device: D, - link_up: bool, - config: Option, - #[cfg(feature = "dhcpv4")] - dhcp_socket: Option, -} - -pub(crate) struct SocketStack { - pub(crate) sockets: SocketSet<'static>, - pub(crate) iface: Interface<'static>, - pub(crate) waker: WakerRegistration, - next_local_port: u16, -} - -impl Stack { - pub fn new( - mut device: D, - config: ConfigStrategy, - resources: &'static mut StackResources, - random_seed: u64, - ) -> Self { - #[cfg(feature = "medium-ethernet")] - let medium = device.capabilities().medium; - - #[cfg(feature = "medium-ethernet")] - let ethernet_addr = if medium == Medium::Ethernet { - device.ethernet_address() - } else { - [0, 0, 0, 0, 0, 0] - }; - - let mut b = InterfaceBuilder::new(); - b = b.ip_addrs(&mut resources.addresses[..]); - b = b.random_seed(random_seed); - - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); - b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); - b = b.routes(Routes::new(&mut resources.routes[..])); - } - - let iface = b.finalize(&mut DeviceAdapter { - inner: &mut device, - cx: None, - }); - - let sockets = SocketSet::new(&mut resources.sockets[..]); - - let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - - let mut inner = Inner { - device, - link_up: false, - config: None, - #[cfg(feature = "dhcpv4")] - dhcp_socket: None, - }; - let mut socket = SocketStack { - sockets, - iface, - waker: WakerRegistration::new(), - next_local_port, - }; - - match config { - ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), - #[cfg(feature = "dhcpv4")] - ConfigStrategy::Dhcp => { - let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); - inner.dhcp_socket = Some(handle); - } - } - - Self { - socket: RefCell::new(socket), - inner: RefCell::new(inner), - } - } - - fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { - f(&*self.socket.borrow(), &*self.inner.borrow()) - } - - fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { - f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) - } - - pub fn ethernet_address(&self) -> [u8; 6] { - self.with(|_s, i| i.device.ethernet_address()) - } - - pub fn is_link_up(&self) -> bool { - self.with(|_s, i| i.link_up) - } - - pub fn is_config_up(&self) -> bool { - self.with(|_s, i| i.config.is_some()) - } - - pub fn config(&self) -> Option { - self.with(|_s, i| i.config.clone()) - } - - pub async fn run(&self) -> ! { - poll_fn(|cx| { - self.with_mut(|s, i| i.poll(cx, s)); - Poll::<()>::Pending - }) - .await; - unreachable!() - } -} - -impl SocketStack { - #[allow(clippy::absurd_extreme_comparisons)] - pub fn get_local_port(&mut self) -> u16 { - let res = self.next_local_port; - self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; - res - } -} - -impl Inner { - fn apply_config(&mut self, s: &mut SocketStack, config: Config) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - - debug!("Acquired IP configuration:"); - - debug!(" IP address: {}", config.address); - self.set_ipv4_addr(s, config.address); - - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - if let Some(gateway) = config.gateway { - debug!(" Default gateway: {}", gateway); - s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); - } else { - debug!(" Default gateway: None"); - s.iface.routes_mut().remove_default_ipv4_route(); - } - } - for (i, s) in config.dns_servers.iter().enumerate() { - debug!(" DNS server {}: {}", i, s); - } - - self.config = Some(config) - } - - #[allow(unused)] // used only with dhcp - fn unapply_config(&mut self, s: &mut SocketStack) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - - debug!("Lost IP configuration"); - self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - s.iface.routes_mut().remove_default_ipv4_route(); - } - self.config = None - } - - fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { - s.iface.update_ip_addrs(|addrs| { - let dest = addrs.iter_mut().next().unwrap(); - *dest = IpCidr::Ipv4(cidr); - }); - } - - fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { - s.waker.register(cx.waker()); - - let timestamp = instant_to_smoltcp(Instant::now()); - let mut smoldev = DeviceAdapter { - cx: Some(cx), - inner: &mut self.device, - }; - if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { - // If poll() returns error, it may not be done yet, so poll again later. - cx.waker().wake_by_ref(); - return; - } - - // Update link up - let old_link_up = self.link_up; - self.link_up = self.device.link_state(cx) == LinkState::Up; - - // Print when changed - if old_link_up != self.link_up { - info!("link_up = {:?}", self.link_up); - } - - #[cfg(feature = "dhcpv4")] - if let Some(dhcp_handle) = self.dhcp_socket { - let socket = s.sockets.get_mut::(dhcp_handle); - - if self.link_up { - match socket.poll() { - None => {} - Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), - Some(dhcpv4::Event::Configured(config)) => { - let config = Config { - address: config.address, - gateway: config.router, - dns_servers: config.dns_servers, - }; - self.apply_config(s, config) - } - } - } else if old_link_up { - socket.reset(); - self.unapply_config(s); - } - } - //if old_link_up || self.link_up { - // self.poll_configurator(timestamp) - //} - - if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { - let t = Timer::at(instant_from_smoltcp(poll_at)); - pin_mut!(t); - if t.poll(cx).is_ready() { - cx.waker().wake_by_ref(); - } - } - } -} - -fn instant_to_smoltcp(instant: Instant) -> SmolInstant { - SmolInstant::from_millis(instant.as_millis() as i64) -} - -fn instant_from_smoltcp(instant: SmolInstant) -> Instant { - Instant::from_millis(instant.total_millis() as u64) -} diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0ed4b81e..0dc8da73 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -8,9 +8,8 @@ use smoltcp::socket::tcp; use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use super::stack::Stack; use crate::device::Device; -use crate::stack::SocketStack; +use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 4ddad77d..2f5334df 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -7,8 +7,7 @@ use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use super::stack::SocketStack; -use crate::{Device, Stack}; +use crate::{Device, SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From e9219405ca04e23b6543fb841fd97df54cf72f94 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 16:03:03 +0100 Subject: [PATCH 215/374] usb/cdc-ncm: add embassy-net Device implementation. --- embassy-usb/Cargo.toml | 1 + embassy-usb/src/class/cdc_ncm/embassy_net.rs | 449 ++++++++++++++++++ .../src/class/{cdc_ncm.rs => cdc_ncm/mod.rs} | 18 + examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/usb_ethernet.rs | 139 +----- examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/usb_ethernet.rs | 140 +----- examples/stm32l5/Cargo.toml | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 140 +----- 9 files changed, 522 insertions(+), 371 deletions(-) create mode 100644 embassy-usb/src/class/cdc_ncm/embassy_net.rs rename embassy-usb/src/class/{cdc_ncm.rs => cdc_ncm/mod.rs} (94%) diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index b59ba8a2..1e72ce68 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,6 +19,7 @@ default = ["usbd-hid"] 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" } +embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs new file mode 100644 index 00000000..60bbfd8d --- /dev/null +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -0,0 +1,449 @@ +use core::cell::RefCell; +use core::mem::MaybeUninit; +use core::task::Context; + +use embassy_futures::select::{select, Either}; +use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::waitqueue::WakerRegistration; +use embassy_usb_driver::Driver; + +use super::{CdcNcmClass, Receiver, Sender}; + +pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { + rx: [PacketBuf; N_RX], + tx: [PacketBuf; N_TX], + inner: MaybeUninit>, +} + +impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { + const NEW_PACKET: PacketBuf = PacketBuf::new(); + + pub const fn new() -> Self { + Self { + rx: [Self::NEW_PACKET; N_RX], + tx: [Self::NEW_PACKET; N_TX], + inner: MaybeUninit::uninit(), + } + } +} + +struct StateInner<'d, const MTU: usize> { + rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + link_state: Mutex>, +} + +/// State of the LinkState +struct LinkStateState { + state: LinkState, + waker: WakerRegistration, +} + +pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { + tx_usb: Sender<'d, D>, + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + rx_usb: Receiver<'d, D>, + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { + pub async fn run(mut self) -> ! { + let rx_fut = async move { + loop { + trace!("WAITING for connection"); + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = LinkState::Down; + s.waker.wake(); + }); + + self.rx_usb.wait_connection().await.unwrap(); + + trace!("Connected"); + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = LinkState::Up; + s.waker.wake(); + }); + + loop { + let p = self.rx_chan.send().await; + match self.rx_usb.read_packet(&mut p.buf).await { + Ok(n) => { + p.len = n; + self.rx_chan.send_done(); + } + Err(e) => { + warn!("error reading packet: {:?}", e); + break; + } + }; + } + } + }; + let tx_fut = async move { + loop { + let p = self.tx_chan.recv().await; + if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { + warn!("Failed to TX packet: {:?}", e); + } + self.tx_chan.recv_done(); + } + }; + match select(rx_fut, tx_fut).await { + Either::First(x) => x, + Either::Second(x) => x, + } + } +} + +impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { + pub fn into_embassy_net_device( + self, + state: &'d mut State<'d, MTU, N_RX, N_TX>, + ethernet_address: [u8; 6], + ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { + let (tx_usb, rx_usb) = self.split(); + + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header + caps.medium = Medium::Ethernet; + + let state = state.inner.write(StateInner { + rx: zerocopy_channel::Channel::new(&mut state.rx[..]), + tx: zerocopy_channel::Channel::new(&mut state.tx[..]), + link_state: Mutex::new(RefCell::new(LinkStateState { + state: LinkState::Down, + waker: WakerRegistration::new(), + })), + }); + + let (rx_sender, rx_receiver) = state.rx.split(); + let (tx_sender, tx_receiver) = state.tx.split(); + + ( + Runner { + tx_usb, + tx_chan: tx_receiver, + rx_usb, + rx_chan: rx_sender, + link_state: &state.link_state, + }, + Device { + caps, + ethernet_address, + link_state: &state.link_state, + rx: rx_receiver, + tx: tx_sender, + }, + ) + } +} + +pub struct PacketBuf { + len: usize, + buf: [u8; MTU], +} + +impl PacketBuf { + pub const fn new() -> Self { + Self { len: 0, buf: [0; MTU] } + } +} + +pub struct Device<'d, const MTU: usize> { + rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, + caps: DeviceCapabilities, + ethernet_address: [u8; 6], +} + +impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { + type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; + type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { + Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) + } else { + None + } + } + + /// Construct a transmit token. + fn transmit(&mut self, cx: &mut Context) -> Option> { + if self.tx.poll_send(cx).is_ready() { + Some(TxToken { tx: self.tx.borrow() }) + } else { + None + } + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> DeviceCapabilities { + self.caps.clone() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.ethernet_address + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.waker.register(cx.waker()); + s.state + }) + } +} + +pub struct RxToken<'a, const MTU: usize> { + rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.try_recv()); + let r = f(&mut pkt.buf[..pkt.len]); + self.rx.recv_done(); + r + } +} + +pub struct TxToken<'a, const MTU: usize> { + tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { + fn consume(mut self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.try_send()); + let r = f(&mut pkt.buf[..len]); + pkt.len = len; + self.tx.send_done(); + r + } +} + +mod zerocopy_channel { + use core::cell::RefCell; + use core::future::poll_fn; + use core::marker::PhantomData; + use core::task::{Context, Poll}; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use embassy_sync::blocking_mutex::Mutex; + use embassy_sync::waitqueue::WakerRegistration; + + pub struct Channel<'a, M: RawMutex, T> { + buf: *mut T, + phantom: PhantomData<&'a mut T>, + state: Mutex>, + } + + impl<'a, M: RawMutex, T> Channel<'a, M, T> { + pub fn new(buf: &'a mut [T]) -> Self { + let len = buf.len(); + assert!(len != 0); + + Self { + buf: buf.as_mut_ptr(), + phantom: PhantomData, + state: Mutex::new(RefCell::new(State { + len, + front: 0, + back: 0, + full: false, + send_waker: WakerRegistration::new(), + recv_waker: WakerRegistration::new(), + })), + } + } + + pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { + (Sender { channel: self }, Receiver { channel: self }) + } + } + + pub struct Sender<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Sender<'a, M, T> { + pub fn borrow(&mut self) -> Sender<'_, M, T> { + Sender { channel: self.channel } + } + + pub fn try_send(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn send(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(i), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn send_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().push_done()) + } + } + pub struct Receiver<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Receiver<'a, M, T> { + pub fn borrow(&mut self) -> Receiver<'_, M, T> { + Receiver { channel: self.channel } + } + + pub fn try_recv(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn recv(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(i), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn recv_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().pop_done()) + } + } + + struct State { + len: usize, + + /// Front index. Always 0..=(N-1) + front: usize, + /// Back index. Always 0..=(N-1). + back: usize, + + /// Used to distinguish "empty" and "full" cases when `front == back`. + /// May only be `true` if `front == back`, always `false` otherwise. + full: bool, + + send_waker: WakerRegistration, + recv_waker: WakerRegistration, + } + + impl State { + fn increment(&self, i: usize) -> usize { + if i + 1 == self.len { + 0 + } else { + i + 1 + } + } + + fn is_full(&self) -> bool { + self.full + } + + fn is_empty(&self) -> bool { + self.front == self.back && !self.full + } + + fn push_index(&mut self) -> Option { + match self.is_full() { + true => None, + false => Some(self.back), + } + } + + fn push_done(&mut self) { + assert!(!self.is_full()); + self.back = self.increment(self.back); + if self.back == self.front { + self.full = true; + } + self.send_waker.wake(); + } + + fn pop_index(&mut self) -> Option { + match self.is_empty() { + true => None, + false => Some(self.front), + } + } + + fn pop_done(&mut self) { + assert!(!self.is_empty()); + self.front = self.increment(self.front); + self.full = false; + self.recv_waker.wake(); + } + } +} diff --git a/embassy-usb/src/class/cdc_ncm.rs b/embassy-usb/src/class/cdc_ncm/mod.rs similarity index 94% rename from embassy-usb/src/class/cdc_ncm.rs rename to embassy-usb/src/class/cdc_ncm/mod.rs index a39b87e9..2ee47f68 100644 --- a/embassy-usb/src/class/cdc_ncm.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -1,3 +1,18 @@ +/// CDC-NCM, aka Ethernet over USB. +/// +/// # Compatibility +/// +/// Windows: NOT supported in Windows 10. Supported in Windows 11. +/// +/// Linux: Well-supported since forever. +/// +/// Android: Support for CDC-NCM is spotty and varies across manufacturers. +/// +/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. +/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), +/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. +/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 +/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; @@ -6,6 +21,9 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; use crate::Builder; +#[cfg(feature = "embassy-net")] +pub mod embassy_net; + /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 8b95ac3a..a980a505 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -16,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 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 = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index de93a2b4..e5f70452 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -3,19 +3,16 @@ #![feature(type_alias_impl_trait)] use core::mem; -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Waker; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; 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::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; use embedded_io::asynch::Write; use static_cell::StaticCell; @@ -27,56 +24,25 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } +const MTU: usize = 1514; + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } #[embassy_executor::task] -async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { - loop { - warn!("WAITING for connection"); - LINK_UP.store(false, Ordering::Relaxed); - - class.wait_connection().await.unwrap(); - - warn!("Connected"); - LINK_UP.store(true, Ordering::Relaxed); - - loop { - let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); - let n = match class.read_packet(&mut p[..]).await { - Ok(n) => n, - Err(e) => { - warn!("error reading packet: {:?}", e); - break; - } - }; - - let buf = p.slice(0..n); - if RX_CHANNEL.try_send(buf).is_err() { - warn!("Failed pushing rx'd packet to channel."); - } - } - } +async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { + class.run().await } #[embassy_executor::task] -async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { - loop { - let pkt = TX_CHANNEL.recv().await; - if let Err(e) = class.write_packet(&pkt[..]).await { - warn!("Failed to TX packet: {:?}", e); - } - } -} - -#[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -108,55 +74,32 @@ async fn main(spawner: Spawner) { config.device_sub_class = 0x02; config.device_protocol = 0x01; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 128], - serial_state: State<'static>, - } - let res: &mut Resources = singleton!(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 128], - serial_state: State::new(), - }); - // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], None, ); - // WARNINGS for Android ethernet tethering: - // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. - // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), - // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. - // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 - // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 - // Our MAC addr. let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (tx, rx) = class.split(); - unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); - unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -172,7 +115,6 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let device = Device { mac_addr: our_mac_addr }; let stack = &*singleton!(Stack::new( device, config, @@ -225,50 +167,3 @@ async fn main(spawner: Spawner) { } } } - -static TX_CHANNEL: Channel = Channel::new(); -static RX_CHANNEL: Channel = Channel::new(); -static LINK_UP: AtomicBool = AtomicBool::new(false); - -struct Device { - mac_addr: [u8; 6], -} - -impl embassy_net::Device for Device { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match LINK_UP.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if TX_CHANNEL.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive<'a>(&mut self) -> Option { - RX_CHANNEL.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index dfbc2642..31a08cfb 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,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", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } 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" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 1057fe7f..d0aec874 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -2,18 +2,14 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Waker; - use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; 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::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; use embedded_io::asynch::Write; use static_cell::StaticCell; @@ -25,56 +21,25 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } +const MTU: usize = 1514; + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } #[embassy_executor::task] -async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { - loop { - warn!("WAITING for connection"); - LINK_UP.store(false, Ordering::Relaxed); - - class.wait_connection().await.unwrap(); - - warn!("Connected"); - LINK_UP.store(true, Ordering::Relaxed); - - loop { - let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); - let n = match class.read_packet(&mut p[..]).await { - Ok(n) => n, - Err(e) => { - warn!("error reading packet: {:?}", e); - break; - } - }; - - let buf = p.slice(0..n); - if RX_CHANNEL.try_send(buf).is_err() { - warn!("Failed pushing rx'd packet to channel."); - } - } - } +async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { + class.run().await } #[embassy_executor::task] -async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { - loop { - let pkt = TX_CHANNEL.recv().await; - if let Err(e) = class.write_packet(&pkt[..]).await { - warn!("Failed to TX packet: {:?}", e); - } - } -} - -#[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -100,55 +65,32 @@ async fn main(spawner: Spawner) { config.device_sub_class = 0x02; config.device_protocol = 0x01; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 128], - serial_state: State<'static>, - } - let res: &mut Resources = singleton!(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 128], - serial_state: State::new(), - }); - // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], None, ); - // WARNINGS for Android ethernet tethering: - // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. - // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), - // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. - // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 - // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 - // Our MAC addr. let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (tx, rx) = class.split(); - unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); - unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -161,7 +103,6 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let device = Device { mac_addr: our_mac_addr }; let stack = &*singleton!(Stack::new( device, config, @@ -214,50 +155,3 @@ async fn main(spawner: Spawner) { } } } - -static TX_CHANNEL: Channel = Channel::new(); -static RX_CHANNEL: Channel = Channel::new(); -static LINK_UP: AtomicBool = AtomicBool::new(false); - -struct Device { - mac_addr: [u8; 6], -} - -impl embassy_net::Device for Device { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match LINK_UP.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if TX_CHANNEL.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive<'a>(&mut self) -> Option { - RX_CHANNEL.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 73ad5078..bb851531 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,7 +11,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", "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 = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } 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 4f36d3f5..b49329ea 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -2,20 +2,16 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Waker; - use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; 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::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; use embedded_io::asynch::Write; use rand_core::RngCore; @@ -28,56 +24,25 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } +const MTU: usize = 1514; + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } #[embassy_executor::task] -async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { - loop { - warn!("WAITING for connection"); - LINK_UP.store(false, Ordering::Relaxed); - - class.wait_connection().await.unwrap(); - - warn!("Connected"); - LINK_UP.store(true, Ordering::Relaxed); - - loop { - let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); - let n = match class.read_packet(&mut p[..]).await { - Ok(n) => n, - Err(e) => { - warn!("error reading packet: {:?}", e); - break; - } - }; - - let buf = p.slice(0..n); - if RX_CHANNEL.try_send(buf).is_err() { - warn!("Failed pushing rx'd packet to channel."); - } - } - } +async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { + class.run().await } #[embassy_executor::task] -async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { - loop { - let pkt = TX_CHANNEL.recv().await; - if let Err(e) = class.write_packet(&pkt[..]).await { - warn!("Failed to TX packet: {:?}", e); - } - } -} - -#[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -106,55 +71,32 @@ async fn main(spawner: Spawner) { config.device_sub_class = 0x02; config.device_protocol = 0x01; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 128], - serial_state: State<'static>, - } - let res: &mut Resources = singleton!(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 128], - serial_state: State::new(), - }); - // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], None, ); - // WARNINGS for Android ethernet tethering: - // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. - // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), - // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. - // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 - // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 - // Our MAC addr. let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (tx, rx) = class.split(); - unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); - unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -168,7 +110,6 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let device = Device { mac_addr: our_mac_addr }; let stack = &*singleton!(Stack::new( device, config, @@ -221,50 +162,3 @@ async fn main(spawner: Spawner) { } } } - -static TX_CHANNEL: Channel = Channel::new(); -static RX_CHANNEL: Channel = Channel::new(); -static LINK_UP: AtomicBool = AtomicBool::new(false); - -struct Device { - mac_addr: [u8; 6], -} - -impl embassy_net::Device for Device { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match LINK_UP.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if TX_CHANNEL.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive<'a>(&mut self) -> Option { - RX_CHANNEL.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} From 8f3065210927b6e92f6d727741189155b2eab91e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Dec 2022 03:18:45 +0100 Subject: [PATCH 216/374] stm32/eth_v1: update to new embassy-net trait, remove PeripheralMutex. --- embassy-stm32/src/eth/mod.rs | 121 +++++++- embassy-stm32/src/eth/v1/descriptors.rs | 21 -- embassy-stm32/src/eth/v1/mod.rs | 391 ++++++++++-------------- embassy-stm32/src/eth/v1/rx_desc.rs | 210 +++++-------- embassy-stm32/src/eth/v1/tx_desc.rs | 157 +++------- examples/stm32f7/src/bin/eth.rs | 50 +-- 6 files changed, 424 insertions(+), 526 deletions(-) delete mode 100644 embassy-stm32/src/eth/v1/descriptors.rs diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 76a3dfab..fd1b4853 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -1,14 +1,131 @@ #![macro_use] +#![cfg_attr(not(feature = "embassy-net"), allow(unused))] -#[cfg(feature = "net")] #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; pub mod generic_smi; -#[cfg(feature = "net")] pub use _version::*; +use embassy_sync::waitqueue::AtomicWaker; +#[allow(unused)] +const MTU: usize = 1514; +const TX_BUFFER_SIZE: usize = 1514; +const RX_BUFFER_SIZE: usize = 1536; + +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub(crate) struct Packet([u8; N]); + +pub struct PacketQueue { + tx_desc: [TDes; TX], + rx_desc: [RDes; RX], + tx_buf: [Packet; TX], + rx_buf: [Packet; RX], +} + +impl PacketQueue { + pub const fn new() -> Self { + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + } + } +} + +static WAKER: AtomicWaker = AtomicWaker::new(); + +#[cfg(feature = "embassy-net")] +mod embassy_net_impl { + use core::task::Context; + + use embassy_net::device::{Device, DeviceCapabilities, LinkState}; + + use super::*; + + impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { + type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + WAKER.register(cx.waker()); + if self.rx.available().is_some() && self.tx.available().is_some() { + Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + } else { + None + } + } + + fn transmit(&mut self, cx: &mut Context) -> Option> { + WAKER.register(cx.waker()); + if self.tx.available().is_some() { + Some(TxToken { tx: &mut self.tx }) + } else { + None + } + } + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(self.tx.len()); + caps + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // TODO: wake cx.waker on link state change + cx.waker().wake_by_ref(); + if P::poll_link(self) { + LinkState::Up + } else { + LinkState::Down + } + } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } + } + + pub struct RxToken<'a, 'd> { + rx: &'a mut RDesRing<'d>, + } + + impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.available()); + let r = f(pkt); + self.rx.pop_packet(); + r + } + } + + pub struct TxToken<'a, 'd> { + tx: &'a mut TDesRing<'d>, + } + + impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.available()); + let r = f(&mut pkt[..len]); + self.tx.transmit(len); + r + } + } +} /// Station Management Interface (SMI) on an ethernet PHY /// /// # Safety diff --git a/embassy-stm32/src/eth/v1/descriptors.rs b/embassy-stm32/src/eth/v1/descriptors.rs deleted file mode 100644 index 25f21ce1..00000000 --- a/embassy-stm32/src/eth/v1/descriptors.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::eth::_version::rx_desc::RDesRing; -use crate::eth::_version::tx_desc::TDesRing; - -pub struct DescriptorRing { - pub(crate) tx: TDesRing, - pub(crate) rx: RDesRing, -} - -impl DescriptorRing { - pub const fn new() -> Self { - Self { - tx: TDesRing::new(), - rx: RDesRing::new(), - } - } - - pub fn init(&mut self) { - self.tx.init(); - self.rx.init(); - } -} diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 38629a93..de36d3da 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -1,14 +1,17 @@ // The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth). -use core::marker::PhantomData; +mod rx_desc; +mod tx_desc; + use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; -use embassy_sync::waitqueue::AtomicWaker; +use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; +pub(crate) use self::rx_desc::{RDes, RDesRing}; +pub(crate) use self::tx_desc::{TDes, TDesRing}; +use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; use crate::gpio::{AnyPin, Speed}; #[cfg(eth_v1a)] @@ -18,29 +21,16 @@ use crate::pac::SYSCFG; use crate::pac::{ETH, RCC}; use crate::Peripheral; -mod descriptors; -mod rx_desc; -mod tx_desc; +pub struct Ethernet<'d, T: Instance, P: PHY> { + _peri: PeripheralRef<'d, T>, + pub(crate) tx: TDesRing<'d>, + pub(crate) rx: RDesRing<'d>, -use descriptors::DescriptorRing; -use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; - -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 const fn new() -> Self { - Self(StateStorage::new()) - } -} - -pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { - state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, pins: [PeripheralRef<'d, AnyPin>; 9], _phy: P, clock_range: Cr, phy_addr: u8, - mac_addr: [u8; 6], + pub(crate) mac_addr: [u8; 6], } #[cfg(eth_v1a)] @@ -82,10 +72,10 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { /// safety: the returned instance is not leak-safe - pub unsafe fn new( - state: &'d mut State<'d, T, TX, RX>, + pub fn new( + queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, interrupt: impl Peripheral

+ 'd, ref_clk: impl Peripheral

> + 'd, @@ -101,134 +91,131 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - #[cfg(eth_v1a)] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_afioen(true)); + unsafe { + // Enable the necessary Clocks + // NOTE(unsafe) We have exclusive access to the registers + #[cfg(eth_v1a)] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_afioen(true)); - // Select RMII (Reduced Media Independent Interface) - // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); + // Select RMII (Reduced Media Independent Interface) + // Must be done prior to enabling peripheral clock + AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); - RCC.ahbenr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - }); - - #[cfg(any(eth_v1b, eth_v1c))] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); + RCC.ahbenr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); }); - // RMII (Reduced Media Independent Interface) - SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); - }); + #[cfg(any(eth_v1b, eth_v1c))] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_syscfgen(true)); + RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); - #[cfg(eth_v1a)] - { - config_in_pins!(ref_clk, rx_d0, rx_d1); - config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); - } + // RMII (Reduced Media Independent Interface) + SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); + }); - #[cfg(any(eth_v1b, eth_v1c))] - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - - // NOTE(unsafe) We are ourselves not leak-safe. - let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); - - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - - // Reset and wait - dma.dmabmr().modify(|w| w.set_sr(true)); - while dma.dmabmr().read().sr() {} - - mac.maccr().modify(|w| { - w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times - w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping - w.set_fes(Fes::FES100); // fast ethernet speed - w.set_dm(Dm::FULLDUPLEX); // full duplex - // TODO: Carrier sense ? ECRSFD - }); - - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_maca0l( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); - - // pause time - mac.macfcr().modify(|w| w.set_pt(0x100)); - - // Transfer and Forward, Receive and Forward - dma.dmaomr().modify(|w| { - w.set_tsf(Tsf::STOREFORWARD); - w.set_rsf(Rsf::STOREFORWARD); - }); - - dma.dmabmr().modify(|w| { - w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? - }); - - // TODO MTU size setting not found for v1 ethernet, check if correct - - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; - - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), - 25..=34 => Cr::CR_20_35, // Divide by 16 - 35..=59 => Cr::CR_35_60, // Divide by 26 - 60..=99 => Cr::CR_60_100, // Divide by 42 - 100..=149 => Cr::CR_100_150, // Divide by 62 - 150..=216 => Cr::CR_150_168, // Divide by 102 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + #[cfg(eth_v1a)] + { + config_in_pins!(ref_clk, rx_d0, rx_d1); + config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); } - }; - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; + #[cfg(any(eth_v1b, eth_v1c))] + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - let mut this = Self { - state, - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - }; + // NOTE(unsafe) We have exclusive access to the registers + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); - this.state.with(|s| { - s.desc_ring.init(); + // Reset and wait + dma.dmabmr().modify(|w| w.set_sr(true)); + while dma.dmabmr().read().sr() {} + + mac.maccr().modify(|w| { + w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times + w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping + w.set_fes(Fes::FES100); // fast ethernet speed + w.set_dm(Dm::FULLDUPLEX); // full duplex + // TODO: Carrier sense ? ECRSFD + }); + + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_maca0l( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); + + // pause time + mac.macfcr().modify(|w| w.set_pt(0x100)); + + // Transfer and Forward, Receive and Forward + dma.dmaomr().modify(|w| { + w.set_tsf(Tsf::STOREFORWARD); + w.set_rsf(Rsf::STOREFORWARD); + }); + + dma.dmabmr().modify(|w| { + w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? + }); + + // TODO MTU size setting not found for v1 ethernet, check if correct + + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = crate::rcc::get_freqs().ahb1; + let hclk_mhz = hclk.0 / 1_000_000; + + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), + 25..=34 => Cr::CR_20_35, // Divide by 16 + 35..=59 => Cr::CR_35_60, // Divide by 26 + 60..=99 => Cr::CR_60_100, // Divide by 42 + 100..=149 => Cr::CR_100_150, // Divide by 62 + 150..=216 => Cr::CR_150_168, // Divide by 102 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; + + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; + + let mut this = Self { + _peri: peri, + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + }; fence(Ordering::SeqCst); @@ -245,23 +232,45 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, w.set_sr(DmaomrSr::STARTED); // start receiving channel }); + this.rx.demand_poll(); + // Enable interrupts dma.dmaier().modify(|w| { w.set_nise(true); w.set_rie(true); w.set_tie(true); }); - }); - P::phy_reset(&mut this); - P::phy_init(&mut this); - this + P::phy_reset(&mut this); + P::phy_init(&mut this); + + interrupt.set_handler(Self::on_interrupt); + interrupt.enable(); + + this + } + } + + fn on_interrupt(_cx: *mut ()) { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmasr().modify(|w| { + w.set_ts(true); + w.set_rs(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmasr().read(); + dma.dmasr().read(); + } } } -unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement - for Ethernet<'d, T, P, TX, RX> -{ +unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` unsafe { @@ -297,44 +306,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa } } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { - fn is_transmit_ready(&mut self) -> bool { - self.state.with(|s| s.desc_ring.tx.available()) - } - - fn transmit(&mut self, pkt: PacketBuf) { - self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); - } - - fn receive(&mut self) -> Option { - self.state.with(|s| s.desc_ring.rx.pop_packet()) - } - - fn register_waker(&mut self, waker: &Waker) { - WAKER.register(waker); - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(TX.min(RX)); - caps - } - - fn link_state(&mut self) -> LinkState { - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} - -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers unsafe { @@ -361,46 +333,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne }) } } - -//---------------------------------------------------------------------- - -struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { - _peri: PhantomData<&'d mut T>, - desc_ring: DescriptorRing, -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { - pub fn new(_peri: impl Peripheral

+ 'd) -> Self { - Self { - _peri: PhantomData, - desc_ring: DescriptorRing::new(), - } - } -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { - type Interrupt = crate::interrupt::ETH; - - fn on_interrupt(&mut self) { - unwrap!(self.desc_ring.tx.on_interrupt()); - self.desc_ring.rx.on_interrupt(); - - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmasr().modify(|w| { - w.set_ts(true); - w.set_rs(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmasr().read(); - dma.dmasr().read(); - } - } -} - -static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index d482590a..8b8566d9 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs @@ -1,9 +1,9 @@ use core::sync::atomic::{compiler_fence, fence, Ordering}; -use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; -use stm32_metapac::eth::vals::{DmaomrSr, Rpd, Rps}; +use stm32_metapac::eth::vals::{Rpd, Rps}; use vcell::VolatileCell; +use crate::eth::RX_BUFFER_SIZE; use crate::pac::ETH; mod rx_consts { @@ -28,6 +28,8 @@ mod rx_consts { use rx_consts::*; +use super::Packet; + /// Receive Descriptor representation /// /// * rdes0: OWN and Status @@ -35,7 +37,7 @@ use rx_consts::*; /// * rdes2: data buffer address /// * rdes3: next descriptor address #[repr(C)] -struct RDes { +pub(crate) struct RDes { rdes0: VolatileCell, rdes1: VolatileCell, rdes2: VolatileCell, @@ -54,7 +56,7 @@ impl RDes { /// Return true if this RDes is acceptable to us #[inline(always)] - pub fn valid(&self) -> bool { + fn valid(&self) -> bool { // Write-back descriptor is valid if: // // Contains first buffer of packet AND contains last buf of @@ -64,15 +66,16 @@ impl RDes { /// Return true if this RDes is not currently owned by the DMA #[inline(always)] - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us } /// Configures the reception buffer address and length and passed descriptor ownership to the DMA #[inline(always)] - pub fn set_ready(&mut self, buf_addr: u32, buf_len: usize) { - self.rdes1.set(self.rdes1.get() | (buf_len as u32) & RXDESC_1_RBS_MASK); - self.rdes2.set(buf_addr); + fn set_ready(&self, buf: *mut u8) { + self.rdes1 + .set(self.rdes1.get() | (RX_BUFFER_SIZE as u32) & RXDESC_1_RBS_MASK); + self.rdes2.set(buf as u32); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); @@ -88,12 +91,12 @@ impl RDes { // points to next descriptor (RCH) #[inline(always)] - fn set_buffer2(&mut self, buffer: *const u8) { + fn set_buffer2(&self, buffer: *const u8) { self.rdes3.set(buffer as u32); } #[inline(always)] - fn set_end_of_ring(&mut self) { + fn set_end_of_ring(&self) { self.rdes1.set(self.rdes1.get() | RXDESC_1_RER); } @@ -102,7 +105,7 @@ impl RDes { ((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize } - pub fn setup(&mut self, next: Option<&Self>) { + fn setup(&self, next: Option<&Self>, buf: *mut u8) { // Defer this initialization to this function, so we can have `RingEntry` on bss. self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH); @@ -113,8 +116,11 @@ impl RDes { self.set_end_of_ring(); } } + + self.set_ready(buf); } } + /// Running state of the `RxRing` #[derive(PartialEq, Eq, Debug)] pub enum RunningState { @@ -123,116 +129,42 @@ pub enum RunningState { Running, } -impl RunningState { - /// whether self equals to `RunningState::Running` - pub fn is_running(&self) -> bool { - *self == RunningState::Running - } -} - /// Rx ring of descriptors and packets -/// -/// This ring has three major locations that work in lock-step. The DMA will never write to the tail -/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 -/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite -/// a packet still to be passed to the application. -/// -/// nt can't pass r (no alloc) -/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ -/// ^ ^t ^t ^ ^t ^ -/// |r |r |r -/// |nt |nt |nt -/// -/// -/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ -/// ^ ^t ^ ^t ^ ^ ^ ^t -/// |r | |r | | |r -/// |nt |nt |nt -/// -pub(crate) struct RDesRing { - descriptors: [RDes; N], - buffers: [Option; N], - read_index: usize, - next_tail_index: usize, +pub(crate) struct RDesRing<'a> { + descriptors: &'a mut [RDes], + buffers: &'a mut [Packet], + index: usize, } -impl RDesRing { - pub const fn new() -> Self { - const RDES: RDes = RDes::new(); - const BUFFERS: Option = None; +impl<'a> RDesRing<'a> { + pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 1); + assert!(descriptors.len() == buffers.len()); - Self { - descriptors: [RDES; N], - buffers: [BUFFERS; N], - read_index: 0, - next_tail_index: 0, - } - } - - pub(crate) fn init(&mut self) { - assert!(N > 1); - let mut last_index = 0; - for (index, buf) in self.buffers.iter_mut().enumerate() { - let pkt = match PacketBox::new(Packet::new()) { - Some(p) => p, - None => { - if index == 0 { - panic!("Could not allocate at least one buffer for Ethernet receiving"); - } else { - break; - } - } - }; - self.descriptors[index].set_ready(pkt.as_ptr() as u32, pkt.len()); - *buf = Some(pkt); - last_index = index; - } - self.next_tail_index = (last_index + 1) % N; - - // not sure if this is supposed to span all of the descriptor or just those that contain buffers - { - let mut previous: Option<&mut RDes> = None; - for entry in self.descriptors.iter_mut() { - if let Some(prev) = &mut previous { - prev.setup(Some(entry)); - } - previous = Some(entry); - } - - if let Some(entry) = &mut previous { - entry.setup(None); - } + for (i, entry) in descriptors.iter().enumerate() { + entry.setup(descriptors.get(i + 1), buffers[i].0.as_mut_ptr()); } - // Register txdescriptor start + // Register rx descriptor start // NOTE (unsafe) Used for atomic writes unsafe { ETH.ethernet_dma() .dmardlar() - .write(|w| w.0 = &self.descriptors as *const _ as u32); + .write(|w| w.0 = descriptors.as_ptr() as u32); }; // We already have fences in `set_owned`, which is called in `setup` - // Start receive - unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_sr(DmaomrSr::STARTED)) }; - - self.demand_poll(); + Self { + descriptors, + buffers, + index: 0, + } } - fn demand_poll(&self) { + pub(crate) fn demand_poll(&self) { unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) }; } - pub(crate) fn on_interrupt(&mut self) { - // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it - // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt - // which should try to pop a packet... - } - /// Get current `RunningState` fn running_state(&self) -> RunningState { match unsafe { ETH.ethernet_dma().dmasr().read().rps() } { @@ -252,52 +184,52 @@ impl RDesRing { } } - pub(crate) fn pop_packet(&mut self) -> Option { - if !self.running_state().is_running() { + /// Get a received packet if any, or None. + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + if self.running_state() != RunningState::Running { self.demand_poll(); } + // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the // buffer (I think .-.) fence(Ordering::SeqCst); - let read_available = self.descriptors[self.read_index].available(); - let tail_index = (self.next_tail_index + N - 1) % N; - - let pkt = if read_available && self.read_index != tail_index { - let pkt = self.buffers[self.read_index].take(); - let len = self.descriptors[self.read_index].packet_len(); - - assert!(pkt.is_some()); - let valid = self.descriptors[self.read_index].valid(); - - self.read_index = (self.read_index + 1) % N; - if valid { - pkt.map(|p| p.slice(0..len)) - } else { - None + // We might have to process many packets, in case some have been rx'd but are invalid. + loop { + let descriptor = &mut self.descriptors[self.index]; + if !descriptor.available() { + return None; } - } else { - None - }; - // Try to advance the tail_index - if self.next_tail_index != self.read_index { - match PacketBox::new(Packet::new()) { - Some(b) => { - let addr = b.as_ptr() as u32; - let buffer_len = b.len(); - self.buffers[self.next_tail_index].replace(b); - self.descriptors[self.next_tail_index].set_ready(addr, buffer_len); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - self.next_tail_index = (self.next_tail_index + 1) % N; - } - None => {} + // If packet is invalid, pop it and try again. + if !descriptor.valid() { + warn!("invalid packet: {:08x}", descriptor.rdes0.get()); + self.pop_packet(); + continue; } + + break; + } + + let descriptor = &mut self.descriptors[self.index]; + let len = descriptor.packet_len(); + return Some(&mut self.buffers[self.index].0[..len]); + } + + /// Pop the packet previously returned by `available`. + pub(crate) fn pop_packet(&mut self) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); + + self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); + + self.demand_poll(); + + // Increment index. + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 } - pkt } } diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index f2889b55..0e63c544 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs @@ -1,20 +1,10 @@ use core::sync::atomic::{compiler_fence, fence, Ordering}; -use embassy_net::PacketBuf; -use stm32_metapac::eth::vals::St; use vcell::VolatileCell; +use crate::eth::TX_BUFFER_SIZE; use crate::pac::ETH; -#[non_exhaustive] -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - NoBufferAvailable, - // TODO: Break down this error into several others - TransmissionError, -} - /// Transmit and Receive Descriptor fields #[allow(dead_code)] mod tx_consts { @@ -37,6 +27,8 @@ mod tx_consts { } use tx_consts::*; +use super::Packet; + /// Transmit Descriptor representation /// /// * tdes0: control @@ -44,7 +36,7 @@ use tx_consts::*; /// * tdes2: data buffer address /// * tdes3: next descriptor address #[repr(C)] -struct TDes { +pub(crate) struct TDes { tdes0: VolatileCell, tdes1: VolatileCell, tdes2: VolatileCell, @@ -62,7 +54,7 @@ impl TDes { } /// Return true if this TDes is not currently owned by the DMA - pub fn available(&self) -> bool { + fn available(&self) -> bool { (self.tdes0.get() & TXDESC_0_OWN) == 0 } @@ -79,26 +71,26 @@ impl TDes { fence(Ordering::SeqCst); } - fn set_buffer1(&mut self, buffer: *const u8) { + fn set_buffer1(&self, buffer: *const u8) { self.tdes2.set(buffer as u32); } - fn set_buffer1_len(&mut self, len: usize) { + fn set_buffer1_len(&self, len: usize) { self.tdes1 .set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT)); } // points to next descriptor (RCH) - fn set_buffer2(&mut self, buffer: *const u8) { + fn set_buffer2(&self, buffer: *const u8) { self.tdes3.set(buffer as u32); } - fn set_end_of_ring(&mut self) { + fn set_end_of_ring(&self) { self.tdes0.set(self.tdes0.get() | TXDESC_0_TER); } // set up as a part fo the ring buffer - configures the tdes - pub fn setup(&mut self, next: Option<&Self>) { + fn setup(&self, next: Option<&Self>) { // Defer this initialization to this function, so we can have `RingEntry` on bss. self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS); match next { @@ -111,85 +103,58 @@ impl TDes { } } -pub(crate) struct TDesRing { - descriptors: [TDes; N], - buffers: [Option; N], - next_entry: usize, +pub(crate) struct TDesRing<'a> { + descriptors: &'a mut [TDes], + buffers: &'a mut [Packet], + index: usize, } -impl TDesRing { - pub const fn new() -> Self { - const TDES: TDes = TDes::new(); - const BUFFERS: Option = None; - - Self { - descriptors: [TDES; N], - buffers: [BUFFERS; N], - next_entry: 0, - } - } - +impl<'a> TDesRing<'a> { /// Initialise this TDesRing. Assume TDesRing is corrupt - /// - /// The current memory address of the buffers inside this TDesRing - /// will be stored in the descriptors, so ensure the TDesRing is - /// not moved after initialisation. - pub(crate) fn init(&mut self) { - assert!(N > 0); + pub(crate) fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 0); + assert!(descriptors.len() == buffers.len()); - { - let mut previous: Option<&mut TDes> = None; - for entry in self.descriptors.iter_mut() { - if let Some(prev) = &mut previous { - prev.setup(Some(entry)); - } - previous = Some(entry); - } - - if let Some(entry) = &mut previous { - entry.setup(None); - } + for (i, entry) in descriptors.iter().enumerate() { + entry.setup(descriptors.get(i + 1)); } - self.next_entry = 0; // Register txdescriptor start // NOTE (unsafe) Used for atomic writes unsafe { ETH.ethernet_dma() .dmatdlar() - .write(|w| w.0 = &self.descriptors as *const _ as u32); + .write(|w| w.0 = descriptors.as_ptr() as u32); } - // "Preceding reads and writes cannot be moved past subsequent writes." - #[cfg(feature = "fence")] - fence(Ordering::Release); - - // We don't need a compiler fence here because all interactions with `Descriptor` are - // volatiles - - // Start transmission - unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_st(St::STARTED)) }; - } - - /// Return true if a TDes is available for use - pub(crate) fn available(&self) -> bool { - self.descriptors[self.next_entry].available() - } - - pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { - if !self.available() { - return Err(Error::NoBufferAvailable); + Self { + descriptors, + buffers, + index: 0, } + } - let descriptor = &mut self.descriptors[self.next_entry]; + pub(crate) fn len(&self) -> usize { + self.descriptors.len() + } - let pkt_len = pkt.len(); - let address = pkt.as_ptr() as *const u8; + /// Return the next available packet buffer for transmitting, or None + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + let descriptor = &mut self.descriptors[self.index]; + if descriptor.available() { + Some(&mut self.buffers[self.index].0) + } else { + None + } + } - descriptor.set_buffer1(address); - descriptor.set_buffer1_len(pkt_len); + /// Transmit the packet written in a buffer returned by `available`. + pub(crate) fn transmit(&mut self, len: usize) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); - self.buffers[self.next_entry].replace(pkt); + descriptor.set_buffer1(self.buffers[self.index].0.as_ptr()); + descriptor.set_buffer1_len(len); descriptor.set_owned(); @@ -198,36 +163,12 @@ impl TDesRing { // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); - // Move the tail pointer (TPR) to the next descriptor - self.next_entry = (self.next_entry + 1) % N; - + // Move the index to the next descriptor + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 + } // Request the DMA engine to poll the latest tx descriptor unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } - Ok(()) - } - - pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { - let previous = (self.next_entry + N - 1) % N; - let td = &self.descriptors[previous]; - - // DMB to ensure that we are reading an updated value, probably not needed at the hardware - // level, but this is also a hint to the compiler that we're syncing on the buffer. - fence(Ordering::SeqCst); - - let tdes0 = td.tdes0.get(); - - if tdes0 & TXDESC_0_OWN != 0 { - // Transmission isn't done yet, probably a receive interrupt that fired this - return Ok(()); - } - - // Release the buffer - self.buffers[previous].take(); - - if tdes0 & TXDESC_0_ES != 0 { - Err(Error::TransmissionError) - } else { - Ok(()) - } } } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5202edf6..224cc202 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -22,11 +22,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -50,25 +51,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -91,8 +90,8 @@ async fn main(spawner: Spawner) -> ! { info!("Network task initialized"); // Then we can use it! - let mut rx_buffer = [0; 1024]; - let mut tx_buffer = [0; 1024]; + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); @@ -107,8 +106,9 @@ async fn main(spawner: Spawner) -> ! { continue; } info!("connected!"); + let buf = [0; 1024]; loop { - let r = socket.write_all(b"Hello\n").await; + let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); return; From 3005ee0178af46ef8a2b7f8b02f4aea69ed9202b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Dec 2022 02:04:33 +0100 Subject: [PATCH 217/374] stm32/eth_v2: update to new embassy-net trait, remove PeripheralMutex. --- embassy-stm32/src/eth/v2/descriptors.rs | 333 +++++++--------------- embassy-stm32/src/eth/v2/mod.rs | 357 ++++++++++-------------- examples/stm32h7/src/bin/eth.rs | 43 ++- examples/stm32h7/src/bin/eth_client.rs | 43 ++- 4 files changed, 289 insertions(+), 487 deletions(-) diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index c6c06a9c..2426596f 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs @@ -1,19 +1,10 @@ use core::sync::atomic::{fence, Ordering}; -use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; use vcell::VolatileCell; +use crate::eth::{Packet, RX_BUFFER_SIZE, TX_BUFFER_SIZE}; use crate::pac::ETH; -#[non_exhaustive] -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - NoBufferAvailable, - // TODO: Break down this error into several others - TransmissionError, -} - /// Transmit and Receive Descriptor fields #[allow(dead_code)] mod emac_consts { @@ -41,7 +32,7 @@ use emac_consts::*; /// * tdes2: buffer lengths /// * tdes3: control and payload/frame length #[repr(C)] -struct TDes { +pub(crate) struct TDes { tdes0: VolatileCell, tdes1: VolatileCell, tdes2: VolatileCell, @@ -59,41 +50,26 @@ impl TDes { } /// Return true if this TDes is not currently owned by the DMA - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.tdes3.get() & EMAC_DES3_OWN == 0 } } -pub(crate) struct TDesRing { - td: [TDes; N], - buffers: [Option; N], - tdidx: usize, +pub(crate) struct TDesRing<'a> { + descriptors: &'a mut [TDes], + buffers: &'a mut [Packet], + index: usize, } -impl TDesRing { - pub const fn new() -> Self { - const TDES: TDes = TDes::new(); - const BUFFERS: Option = None; +impl<'a> TDesRing<'a> { + /// Initialise this TDesRing. Assume TDesRing is corrupt. + pub fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 0); + assert!(descriptors.len() == buffers.len()); - Self { - td: [TDES; N], - buffers: [BUFFERS; N], - tdidx: 0, - } - } - - /// Initialise this TDesRing. Assume TDesRing is corrupt - /// - /// The current memory address of the buffers inside this TDesRing - /// will be stored in the descriptors, so ensure the TDesRing is - /// not moved after initialisation. - pub(crate) fn init(&mut self) { - assert!(N > 0); - - for td in self.td.iter_mut() { + for td in descriptors.iter_mut() { *td = TDes::new(); } - self.tdidx = 0; // Initialize the pointers in the DMA engine. (There will be a memory barrier later // before the DMA engine is enabled.) @@ -101,80 +77,60 @@ impl TDesRing { unsafe { let dma = ETH.ethernet_dma(); - dma.dmactx_dlar().write(|w| w.0 = &self.td as *const _ as u32); - dma.dmactx_rlr().write(|w| w.set_tdrl((N as u16) - 1)); - dma.dmactx_dtpr().write(|w| w.0 = &self.td[0] as *const _ as u32); + dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); + dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1)); + dma.dmactx_dtpr().write(|w| w.0 = 0); + } + + Self { + descriptors, + buffers, + index: 0, } } - /// Return true if a TDes is available for use - pub(crate) fn available(&self) -> bool { - self.td[self.tdidx].available() + pub(crate) fn len(&self) -> usize { + self.descriptors.len() } - pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { - if !self.available() { - return Err(Error::NoBufferAvailable); + /// Return the next available packet buffer for transmitting, or None + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + let d = &mut self.descriptors[self.index]; + if d.available() { + Some(&mut self.buffers[self.index].0) + } else { + None } - let x = self.tdidx; - let td = &mut self.td[x]; + } - let pkt_len = pkt.len(); - assert!(pkt_len as u32 <= EMAC_TDES2_B1L); - let address = pkt.as_ptr() as u32; + /// Transmit the packet written in a buffer returned by `available`. + pub(crate) fn transmit(&mut self, len: usize) { + let td = &mut self.descriptors[self.index]; + assert!(td.available()); + assert!(len as u32 <= EMAC_TDES2_B1L); // Read format - td.tdes0.set(address); - td.tdes2.set(pkt_len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); + td.tdes0.set(self.buffers[self.index].0.as_ptr() as u32); + td.tdes2.set(len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); // FD: Contains first buffer of packet // LD: Contains last buffer of packet // Give the DMA engine ownership td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN); - self.buffers[x].replace(pkt); - // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store. // This will generate an DMB instruction. // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); - // Move the tail pointer (TPR) to the next descriptor - let x = (x + 1) % N; + self.index = self.index + 1; + if self.index == self.descriptors.len() { + self.index = 0; + } + + // signal DMA it can try again. // NOTE(unsafe) Atomic write - unsafe { - ETH.ethernet_dma() - .dmactx_dtpr() - .write(|w| w.0 = &self.td[x] as *const _ as u32); - } - self.tdidx = x; - Ok(()) - } - - pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { - let previous = (self.tdidx + N - 1) % N; - let td = &self.td[previous]; - - // DMB to ensure that we are reading an updated value, probably not needed at the hardware - // level, but this is also a hint to the compiler that we're syncing on the buffer. - fence(Ordering::SeqCst); - - let tdes3 = td.tdes3.get(); - - if tdes3 & EMAC_DES3_OWN != 0 { - // Transmission isn't done yet, probably a receive interrupt that fired this - return Ok(()); - } - assert!(tdes3 & EMAC_DES3_CTXT == 0); - - // Release the buffer - self.buffers[previous].take(); - - if tdes3 & EMAC_DES3_ES != 0 { - Err(Error::TransmissionError) - } else { - Ok(()) - } + unsafe { ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) } } } @@ -185,7 +141,7 @@ impl TDesRing { /// * rdes2: /// * rdes3: OWN and Status #[repr(C)] -struct RDes { +pub(crate) struct RDes { rdes0: VolatileCell, rdes1: VolatileCell, rdes2: VolatileCell, @@ -204,7 +160,7 @@ impl RDes { /// Return true if this RDes is acceptable to us #[inline(always)] - pub fn valid(&self) -> bool { + fn valid(&self) -> bool { // Write-back descriptor is valid if: // // Contains first buffer of packet AND contains last buf of @@ -215,177 +171,96 @@ impl RDes { /// Return true if this RDes is not currently owned by the DMA #[inline(always)] - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us } #[inline(always)] - pub fn set_ready(&mut self, buf_addr: u32) { - self.rdes0.set(buf_addr); + fn set_ready(&mut self, buf: *mut u8) { + self.rdes0.set(buf as u32); self.rdes3.set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN); } } /// Rx ring of descriptors and packets -/// -/// This ring has three major locations that work in lock-step. The DMA will never write to the tail -/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 -/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite -/// a packet still to be passed to the application. -/// -/// nt can't pass r (no alloc) -/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ -/// ^ ^t ^t ^ ^t ^ -/// |r |r |r -/// |nt |nt |nt -/// -/// -/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ -/// ^ ^t ^ ^t ^ ^ ^ ^t -/// |r | |r | | |r -/// |nt |nt |nt -/// -pub(crate) struct RDesRing { - rd: [RDes; N], - buffers: [Option; N], - read_idx: usize, - next_tail_idx: usize, +pub(crate) struct RDesRing<'a> { + descriptors: &'a mut [RDes], + buffers: &'a mut [Packet], + index: usize, } -impl RDesRing { - pub const fn new() -> Self { - const RDES: RDes = RDes::new(); - const BUFFERS: Option = None; +impl<'a> RDesRing<'a> { + pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 1); + assert!(descriptors.len() == buffers.len()); - Self { - rd: [RDES; N], - buffers: [BUFFERS; N], - read_idx: 0, - next_tail_idx: 0, - } - } - - pub(crate) fn init(&mut self) { - assert!(N > 1); - - for desc in self.rd.iter_mut() { + for (i, desc) in descriptors.iter_mut().enumerate() { *desc = RDes::new(); + desc.set_ready(buffers[i].0.as_mut_ptr()); } - let mut last_index = 0; - for (index, buf) in self.buffers.iter_mut().enumerate() { - let pkt = match PacketBox::new(Packet::new()) { - Some(p) => p, - None => { - if index == 0 { - panic!("Could not allocate at least one buffer for Ethernet receiving"); - } else { - break; - } - } - }; - let addr = pkt.as_ptr() as u32; - *buf = Some(pkt); - self.rd[index].set_ready(addr); - last_index = index; - } - self.next_tail_idx = (last_index + 1) % N; - unsafe { let dma = ETH.ethernet_dma(); - dma.dmacrx_dlar().write(|w| w.0 = self.rd.as_ptr() as u32); - dma.dmacrx_rlr().write(|w| w.set_rdrl((N as u16) - 1)); + dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); + dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1)); + dma.dmacrx_dtpr().write(|w| w.0 = 0); + } - // We manage to allocate all buffers, set the index to the last one, that means - // that the DMA won't consider the last one as ready, because it (unfortunately) - // stops at the tail ptr and wraps at the end of the ring, which means that we - // can't tell it to stop after the last buffer. - let tail_ptr = &self.rd[last_index] as *const _ as u32; - fence(Ordering::Release); - - dma.dmacrx_dtpr().write(|w| w.0 = tail_ptr); + Self { + descriptors, + buffers, + index: 0, } } - pub(crate) fn on_interrupt(&mut self) { - // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it - // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt - // which should try to pop a packet... - } - - pub(crate) fn pop_packet(&mut self) -> Option { + /// Get a received packet if any, or None. + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the // buffer (I think .-.) fence(Ordering::SeqCst); - let read_available = self.rd[self.read_idx].available(); - let tail_index = (self.next_tail_idx + N - 1) % N; - - let pkt = if read_available && self.read_idx != tail_index { - let pkt = self.buffers[self.read_idx].take(); - let len = (self.rd[self.read_idx].rdes3.get() & EMAC_RDES3_PKTLEN) as usize; - - assert!(pkt.is_some()); - let valid = self.rd[self.read_idx].valid(); - - self.read_idx = (self.read_idx + 1) % N; - if valid { - pkt.map(|p| p.slice(0..len)) - } else { - None + // We might have to process many packets, in case some have been rx'd but are invalid. + loop { + let descriptor = &mut self.descriptors[self.index]; + if !descriptor.available() { + return None; } - } else { - None - }; - // Try to advance the tail_idx - if self.next_tail_idx != self.read_idx { - match PacketBox::new(Packet::new()) { - Some(b) => { - let addr = b.as_ptr() as u32; - self.buffers[self.next_tail_idx].replace(b); - self.rd[self.next_tail_idx].set_ready(addr); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - // NOTE(unsafe) atomic write - unsafe { - ETH.ethernet_dma() - .dmacrx_dtpr() - .write(|w| w.0 = &self.rd[self.next_tail_idx] as *const _ as u32); - } - - self.next_tail_idx = (self.next_tail_idx + 1) % N; - } - None => {} + // If packet is invalid, pop it and try again. + if !descriptor.valid() { + warn!("invalid packet: {:08x}", descriptor.rdes0.get()); + self.pop_packet(); + continue; } + + break; } - pkt + + let descriptor = &mut self.descriptors[self.index]; + let len = (descriptor.rdes3.get() & EMAC_RDES3_PKTLEN) as usize; + return Some(&mut self.buffers[self.index].0[..len]); } -} -pub struct DescriptorRing { - pub(crate) tx: TDesRing, - pub(crate) rx: RDesRing, -} + /// Pop the packet previously returned by `available`. + pub(crate) fn pop_packet(&mut self) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); -impl DescriptorRing { - pub const fn new() -> Self { - Self { - tx: TDesRing::new(), - rx: RDesRing::new(), + self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::Release); + + // signal DMA it can try again. + // NOTE(unsafe) Atomic write + unsafe { ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0) } + + // Increment index. + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 } } - - pub fn init(&mut self) { - self.tx.init(); - self.rx.init(); - } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 5b76d1e7..fcb4a296 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,35 +1,28 @@ -use core::marker::PhantomData; +mod descriptors; + use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; -use embassy_sync::waitqueue::AtomicWaker; +pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; +use super::*; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; use crate::pac::{ETH, RCC, SYSCFG}; use crate::Peripheral; -mod descriptors; -use descriptors::DescriptorRing; +const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet -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 const fn new() -> Self { - Self(StateStorage::new()) - } -} -pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { - state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, +pub struct Ethernet<'d, T: Instance, P: PHY> { + _peri: PeripheralRef<'d, T>, + pub(crate) tx: TDesRing<'d>, + pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], _phy: P, clock_range: u8, phy_addr: u8, - mac_addr: [u8; 6], + pub(crate) mac_addr: [u8; 6], } macro_rules! config_pins { @@ -44,10 +37,9 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { - /// safety: the returned instance is not leak-safe - pub unsafe fn new( - state: &'d mut State<'d, T, TX, RX>, +impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { + pub fn new( + queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, interrupt: impl Peripheral

+ 'd, ref_clk: impl Peripheral

> + 'd, @@ -63,126 +55,123 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - critical_section::with(|_| { - RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { - w.set_eth1macen(true); - w.set_eth1txen(true); - w.set_eth1rxen(true); + unsafe { + // Enable the necessary Clocks + // NOTE(unsafe) We have exclusive access to the registers + critical_section::with(|_| { + RCC.apb4enr().modify(|w| w.set_syscfgen(true)); + RCC.ahb1enr().modify(|w| { + w.set_eth1macen(true); + w.set_eth1txen(true); + w.set_eth1rxen(true); + }); + + // RMII + SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); }); - // RMII - SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); - }); + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + // NOTE(unsafe) We have exclusive access to the registers + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); + let mtl = ETH.ethernet_mtl(); - // NOTE(unsafe) We are ourselves not leak-safe. - let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); + // Reset and wait + dma.dmamr().modify(|w| w.set_swr(true)); + while dma.dmamr().read().swr() {} - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); + mac.maccr().modify(|w| { + w.set_ipg(0b000); // 96 bit times + w.set_acs(true); + w.set_fes(true); + w.set_dm(true); + // TODO: Carrier sense ? ECRSFD + }); - // Reset and wait - dma.dmamr().modify(|w| w.set_swr(true)); - while dma.dmamr().read().swr() {} + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_addrlo( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); - mac.maccr().modify(|w| { - w.set_ipg(0b000); // 96 bit times - w.set_acs(true); - w.set_fes(true); - w.set_dm(true); - // TODO: Carrier sense ? ECRSFD - }); + mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_addrlo( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); + // disable all MMC RX interrupts + mac.mmc_rx_interrupt_mask().write(|w| { + w.set_rxcrcerpim(true); + w.set_rxalgnerpim(true); + w.set_rxucgpim(true); + w.set_rxlpiuscim(true); + w.set_rxlpitrcim(true) + }); - mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); + // disable all MMC TX interrupts + mac.mmc_tx_interrupt_mask().write(|w| { + w.set_txscolgpim(true); + w.set_txmcolgpim(true); + w.set_txgpktim(true); + w.set_txlpiuscim(true); + w.set_txlpitrcim(true); + }); - // disable all MMC RX interrupts - mac.mmc_rx_interrupt_mask().write(|w| { - w.set_rxcrcerpim(true); - w.set_rxalgnerpim(true); - w.set_rxucgpim(true); - w.set_rxlpiuscim(true); - w.set_rxlpitrcim(true) - }); + mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); + mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); - // disable all MMC TX interrupts - mac.mmc_tx_interrupt_mask().write(|w| { - w.set_txscolgpim(true); - w.set_txmcolgpim(true); - w.set_txgpktim(true); - w.set_txlpiuscim(true); - w.set_txlpitrcim(true); - }); + dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? + dma.dmacrx_cr().modify(|w| { + w.set_rxpbl(1); // 32 ? + w.set_rbsz(MTU as u16); + }); - mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); - mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = crate::rcc::get_freqs().ahb1; + let hclk_mhz = hclk.0 / 1_000_000; - dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? - dma.dmacrx_cr().modify(|w| { - w.set_rxpbl(1); // 32 ? - w.set_rbsz(MTU as u16); - }); + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=34 => 2, // Divide by 16 + 35..=59 => 3, // Divide by 26 + 60..=99 => 0, // Divide by 42 + 100..=149 => 1, // Divide by 62 + 150..=249 => 4, // Divide by 102 + 250..=310 => 5, // Divide by 124 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=34 => 2, // Divide by 16 - 35..=59 => 3, // Divide by 26 - 60..=99 => 0, // Divide by 42 - 100..=149 => 1, // Divide by 62 - 150..=249 => 4, // Divide by 102 - 250..=310 => 5, // Divide by 124 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") - } - }; - - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; - - let mut this = Self { - state, - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - }; - - this.state.with(|s| { - s.desc_ring.init(); + let mut this = Self { + _peri: peri, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + }; fence(Ordering::SeqCst); @@ -205,17 +194,37 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, w.set_rie(true); w.set_tie(true); }); - }); - P::phy_reset(&mut this); - P::phy_init(&mut this); - this + P::phy_reset(&mut this); + P::phy_init(&mut this); + + interrupt.set_handler(Self::on_interrupt); + interrupt.enable(); + + this + } + } + + fn on_interrupt(_cx: *mut ()) { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmacsr().modify(|w| { + w.set_ti(true); + w.set_ri(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmacsr().read(); + dma.dmacsr().read(); + } } } -unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement - for Ethernet<'d, T, P, TX, RX> -{ +unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` unsafe { @@ -251,44 +260,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa } } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { - fn is_transmit_ready(&mut self) -> bool { - self.state.with(|s| s.desc_ring.tx.available()) - } - - fn transmit(&mut self, pkt: PacketBuf) { - self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); - } - - fn receive(&mut self) -> Option { - self.state.with(|s| s.desc_ring.rx.pop_packet()) - } - - fn register_waker(&mut self, waker: &Waker) { - WAKER.register(waker); - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(TX.min(RX)); - caps - } - - fn link_state(&mut self) -> LinkState { - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} - -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers unsafe { @@ -325,46 +297,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne }) } } - -//---------------------------------------------------------------------- - -struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { - _peri: PhantomData<&'d mut T>, - desc_ring: DescriptorRing, -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { - pub fn new(_peri: impl Peripheral

+ 'd) -> Self { - Self { - _peri: PhantomData, - desc_ring: DescriptorRing::new(), - } - } -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { - type Interrupt = crate::interrupt::ETH; - - fn on_interrupt(&mut self) { - unwrap!(self.desc_ring.tx.on_interrupt()); - self.desc_ring.rx.on_interrupt(); - - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmacsr().modify(|w| { - w.set_ti(true); - w.set_ri(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmacsr().read(); - dma.dmacsr().read(); - } - } -} - -static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 4ccc0b5e..551325ca 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -22,11 +22,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -51,25 +52,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 64fd8414..61a08ae1 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_net::{Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -23,11 +23,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -52,25 +53,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { From 5eae295c8ad8da529490b89f914f2cca07ae6972 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Dec 2022 16:14:50 +0100 Subject: [PATCH 218/374] stm32: rename feature `net` to `embassy-net`. The eth code is always built and available, but has no own API (other than the embassy-net Device impl) to rx/tx packets. We could add this API in the future, so the feature only means "embassy-net support". --- embassy-stm32/Cargo.toml | 1 - examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b7f718c5..0c491ee4 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -77,7 +77,6 @@ stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features [features] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] -net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index f4d674cd..afdf8700 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.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", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 11ce3505..5acf0035 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.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", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } From 790e4e1594d455d06c7303a628172244e78af0da Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Dec 2022 16:18:16 +0100 Subject: [PATCH 219/374] examples/std: update to new embassy-net trait. --- examples/std/src/tuntap.rs | 113 +++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index a0cace7f..bb3e194c 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -1,8 +1,10 @@ use std::io; use std::io::{Read, Write}; use std::os::unix::io::{AsRawFd, RawFd}; +use std::task::Context; use async_io::Async; +use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -125,54 +127,35 @@ impl io::Write for TunTap { pub struct TunTapDevice { device: Async, - waker: Option, } impl TunTapDevice { pub fn new(name: &str) -> io::Result { Ok(Self { device: Async::new(TunTap::new(name)?)?, - waker: None, }) } } -use core::task::Waker; -use std::task::Context; - -use embassy_net::{Device, DeviceCapabilities, LinkState, Packet, PacketBox, PacketBoxExt, PacketBuf}; - impl Device for TunTapDevice { - fn is_transmit_ready(&mut self) -> bool { - true - } + type RxToken<'a> = RxToken where Self: 'a; + type TxToken<'a> = TxToken<'a> where Self: 'a; - fn transmit(&mut self, pkt: PacketBuf) { - // todo handle WouldBlock - match self.device.get_mut().write(&pkt) { - Ok(_) => {} - Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - info!("transmit WouldBlock"); - } - Err(e) => panic!("transmit error: {:?}", e), - } - } - - fn receive(&mut self) -> Option { - let mut pkt = PacketBox::new(Packet::new()).unwrap(); + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let mut buf = vec![0; self.device.get_ref().mtu]; loop { - match self.device.get_mut().read(&mut pkt[..]) { + match self.device.get_mut().read(&mut buf) { Ok(n) => { - return Some(pkt.slice(0..n)); + buf.truncate(n); + return Some(( + RxToken { buffer: buf }, + TxToken { + device: &mut self.device, + }, + )); } Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - let ready = if let Some(w) = self.waker.as_ref() { - let mut cx = Context::from_waker(w); - self.device.poll_readable(&mut cx).is_ready() - } else { - false - }; - if !ready { + if !self.device.poll_readable(cx).is_ready() { return None; } } @@ -181,28 +164,10 @@ impl Device for TunTapDevice { } } - fn register_waker(&mut self, w: &Waker) { - match self.waker { - // Optimization: If both the old and new Wakers wake the same task, we can simply - // keep the old waker, skipping the clone. (In most executor implementations, - // cloning a waker is somewhat expensive, comparable to cloning an Arc). - Some(ref w2) if (w2.will_wake(w)) => {} - _ => { - // clone the new waker and store it - if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { - // We had a waker registered for another task. Wake it, so the other task can - // reregister itself if it's still interested. - // - // If two tasks are waiting on the same thing concurrently, this will cause them - // to wake each other in a loop fighting over this WakerRegistration. This wastes - // CPU but things will still work. - // - // If the user wants to have two tasks waiting on the same thing they should use - // a more appropriate primitive that can store multiple wakers. - old_waker.wake() - } - } - } + fn transmit(&mut self, _cx: &mut Context) -> Option> { + Some(TxToken { + device: &mut self.device, + }) } fn capabilities(&self) -> DeviceCapabilities { @@ -211,7 +176,7 @@ impl Device for TunTapDevice { caps } - fn link_state(&mut self) -> LinkState { + fn link_state(&mut self, _cx: &mut Context) -> LinkState { LinkState::Up } @@ -219,3 +184,41 @@ impl Device for TunTapDevice { [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] } } + +#[doc(hidden)] +pub struct RxToken { + buffer: Vec, +} + +impl device::RxToken for RxToken { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(&mut self.buffer) + } +} + +#[doc(hidden)] +pub struct TxToken<'a> { + device: &'a mut Async, +} + +impl<'a> device::TxToken for TxToken<'a> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = vec![0; len]; + let result = f(&mut buffer); + + // todo handle WouldBlock with async + match self.device.get_mut().write(&buffer) { + Ok(_) => {} + Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!("transmit WouldBlock"), + Err(e) => panic!("transmit error: {:?}", e), + } + + result + } +} From bffa5be2f49fdd5e223df8dae7df88f52bde83e8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 15 Dec 2022 19:43:36 +0100 Subject: [PATCH 220/374] nrf/uart: do not error on zero length transfers. It's a perfectly fine thing to do, should be just a noop. Erroring is really annoying when you're writing a payload to uart that might be zero-length or not. --- embassy-nrf/src/uarte.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 63df1b68..4703c1a5 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -53,7 +53,6 @@ impl Default for Config { #[non_exhaustive] pub enum Error { BufferTooLong, - BufferZeroLength, DMABufferNotInDataMemory, // TODO: add other error variants. } @@ -370,10 +369,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { } pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } + + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -437,10 +437,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { } pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } + + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -550,7 +551,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -603,7 +604,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -672,7 +673,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(0); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -728,7 +729,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(0); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -918,7 +919,6 @@ mod eh1 { fn kind(&self) -> embedded_hal_1::serial::ErrorKind { match *self { Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::BufferZeroLength => embedded_hal_1::serial::ErrorKind::Other, Self::DMABufferNotInDataMemory => embedded_hal_1::serial::ErrorKind::Other, } } From feaeb533fb5963e9d603ccb5143662b21daed2d8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Dec 2022 01:15:32 +0100 Subject: [PATCH 221/374] hal-common/atomic_ring_buffer: fix crashes when len=0 --- embassy-hal-common/src/atomic_ring_buffer.rs | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index c5e44430..3e90b087 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -82,10 +82,11 @@ impl RingBuffer { } pub fn is_full(&self) -> bool { + let len = self.len.load(Ordering::Relaxed); let start = self.start.load(Ordering::Relaxed); let end = self.end.load(Ordering::Relaxed); - self.wrap(end + 1) == start + len == 0 || self.wrap(end + 1) == start } pub fn is_empty(&self) -> bool { @@ -154,7 +155,7 @@ impl<'a> Writer<'a> { let end = self.0.end.load(Ordering::Relaxed); let n = if start <= end { - len - end - (start == 0) as usize + len - end - (start == 0 && len != 0) as usize } else { start - end - 1 }; @@ -328,4 +329,23 @@ mod tests { assert_eq!(rb.is_full(), true); } } + + #[test] + fn zero_len() { + let rb = RingBuffer::new(); + unsafe { + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), true); + + rb.writer().push(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + + rb.reader().pop(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + } + } } From 5b72410828309bd0d37c8f85c0fb4f88d2a105f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Dec 2022 01:16:41 +0100 Subject: [PATCH 222/374] hal-common/atomic_ring_buffer: Add push_slice, pop_slice. --- embassy-hal-common/src/atomic_ring_buffer.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index 3e90b087..a8a6a216 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -135,6 +135,14 @@ impl<'a> Writer<'a> { n != 0 } + /// Get a buffer where data can be pushed to. + /// + /// Equivalent to [`Self::push_buf`] but returns a slice. + pub fn push_slice(&mut self) -> &mut [u8] { + let (data, len) = self.push_buf(); + unsafe { slice::from_raw_parts_mut(data, len) } + } + /// Get a buffer where data can be pushed to. /// /// Write data to the start of the buffer, then call `push_done` with @@ -204,6 +212,14 @@ impl<'a> Reader<'a> { res } + /// Get a buffer where data can be popped from. + /// + /// Equivalent to [`Self::pop_buf`] but returns a slice. + pub fn pop_slice(&mut self) -> &mut [u8] { + let (data, len) = self.pop_buf(); + unsafe { slice::from_raw_parts_mut(data, len) } + } + /// Get a buffer where data can be popped from. /// /// Read data from the start of the buffer, then call `pop_done` with From 849a0e174fa1601236050afb72290174675c585f Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 20 Dec 2022 09:11:39 +0800 Subject: [PATCH 223/374] add convert_to_celsius function in the adc module modify RP2040 adc example to get inside biased bipolar diode voltage, then convert this temperature sensor data into Celsius degree, according to chapter 4.9.5. Temperature Sensor in RP2040 datasheet. --- examples/rp/src/bin/adc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 2a9e9373..25e5126b 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -27,7 +27,12 @@ async fn main(_spawner: Spawner) { let level = adc.read(&mut p28).await; info!("Pin 28 ADC: {}", level); let temp = adc.read_temperature().await; - info!("Temp: {}", temp); + info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } } + +fn convert_to_celsius(raw_temp: u16) -> f32 { + // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 -0.706)/0.001721 as f32 +} \ No newline at end of file From 5ae91ed3b64a086b15a949477a5762a3876307c5 Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 20 Dec 2022 14:59:49 +0800 Subject: [PATCH 224/374] cargo fmt --- examples/rp/src/bin/adc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 25e5126b..4202fd39 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -34,5 +34,5 @@ async fn main(_spawner: Spawner) { fn convert_to_celsius(raw_temp: u16) -> f32 { // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet - 27.0 - (raw_temp as f32 * 3.3 / 4096.0 -0.706)/0.001721 as f32 -} \ No newline at end of file + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 +} From 63122f6d7e9b6a3ec9e246a7171444bde41ce9f0 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:52:40 +0530 Subject: [PATCH 225/374] button controlled example --- .../src/bin/button_controlled_blink.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 examples/stm32f0/src/bin/button_controlled_blink.rs diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs new file mode 100644 index 00000000..e1f22343 --- /dev/null +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -0,0 +1,64 @@ +//! This example showcases how to create task + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{AnyPin, Input, Level, Output, Pin, Pull, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +static BLINK_MS: AtomicU32 = AtomicU32::new(0); + +#[embassy_executor::task] +async fn led_task(led: AnyPin) { + // Configure the LED pin as a push pull ouput and obtain handler. + // On the Nucleo F091RC theres an on-board LED connected to pin PA5. + let mut led = Output::new(led, Level::Low, Speed::Low); + + loop { + let del = BLINK_MS.load(Ordering::Relaxed); + info!("Value of del is {}", del); + Timer::after(Duration::from_millis(del.into())).await; + info!("LED toggling"); + led.toggle(); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + // Initialize and create handle for devicer peripherals + let p = embassy_stm32::init(Default::default()); + + // Configure the button pin and obtain handler. + // On the Nucleo F091RC there is a button connected to pin PC13. + let button = Input::new(p.PC13, Pull::None); + let mut button = ExtiInput::new(button, p.EXTI13); + + // Create and initialize a delay variable to manage delay loop + let mut del_var = 2000; + + // Blink duration value to global context + BLINK_MS.store(del_var, Ordering::Relaxed); + + // Spawn LED blinking task + spawner.spawn(led_task(p.PA5.degrade())).unwrap(); + + loop { + // Check if button got pressed + button.wait_for_rising_edge().await; + info!("rising_edge"); + del_var = del_var - 200; + // If updated delay value drops below 200 then reset it back to starting value + if del_var < 200 { + del_var = 2000; + } + // Updated delay value to global context + BLINK_MS.store(del_var, Ordering::Relaxed); + } +} From c0f3610581164e12446053706b6dbef98b1f1089 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:53:55 +0530 Subject: [PATCH 226/374] added interrupt example --- examples/stm32f0/src/bin/button_exti.rs | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/stm32f0/src/bin/button_exti.rs diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs new file mode 100644 index 00000000..40c0d584 --- /dev/null +++ b/examples/stm32f0/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize and create handle for devicer peripherals + let p = embassy_stm32::init(Default::default()); + // Configure the button pin and obtain handler. + // On the Nucleo F091RC there is a button connected to pin PC13. + let button = Input::new(p.PC13, Pull::Down); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} From 0db3837dccb9c0ee2fdb236937a6a20b63fb89f8 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:54:36 +0530 Subject: [PATCH 227/374] added priority based example --- examples/stm32f0/src/bin/priority.rs | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 examples/stm32f0/src/bin/priority.rs diff --git a/examples/stm32f0/src/bin/priority.rs b/examples/stm32f0/src/bin/priority.rs new file mode 100644 index 00000000..7fed6a77 --- /dev/null +++ b/examples/stm32f0/src/bin/priority.rs @@ -0,0 +1,139 @@ +//! This example showcases how to create multiple Executor instances to run tasks at +//! different priority levels. +//! +//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling +//! there's work in the queue, and `wfe` for waiting for work. +//! +//! Medium and high priority executors run in two interrupts with different priorities. +//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since +//! when there's work the interrupt will trigger and run the executor. +//! +//! Sample output below. Note that high priority ticks can interrupt everything else, and +//! medium priority computations can interrupt low priority computations, making them to appear +//! to take significantly longer time. +//! +//! ```not_rust +//! [med] Starting long computation +//! [med] done in 992 ms +//! [high] tick! +//! [low] Starting long computation +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [low] done in 3972 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! ``` +//! +//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. +//! You will get an output like the following. Note that no computation is ever interrupted. +//! +//! ```not_rust +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [low] Starting long computation +//! [low] done in 992 ms +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! [low] Starting long computation +//! [low] done in 992 ms +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! ``` +//! + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_stm32::interrupt; +use embassy_stm32::interrupt::InterruptExt; +use embassy_time::{Duration, Instant, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run_high() { + loop { + // info!(" [high] tick!"); + Timer::after(Duration::from_ticks(27374)).await; + } +} + +#[embassy_executor::task] +async fn run_med() { + loop { + let start = Instant::now(); + info!(" [med] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(8_000_000); // ~1 second + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() / 33; + info!(" [med] done in {} ms", ms); + + Timer::after(Duration::from_ticks(23421)).await; + } +} + +#[embassy_executor::task] +async fn run_low() { + loop { + let start = Instant::now(); + info!("[low] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(16_000_000); // ~2 seconds + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() / 33; + info!("[low] done in {} ms", ms); + + Timer::after(Duration::from_ticks(32983)).await; + } +} + +static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); +static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_LOW: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + // Initialize and create handle for devicer peripherals + let _p = embassy_stm32::init(Default::default()); + + // High-priority executor: USART1, priority level 6 + let irq = interrupt::take!(USART1); + irq.set_priority(interrupt::Priority::P6); + let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); + let spawner = executor.start(); + unwrap!(spawner.spawn(run_high())); + + // Medium-priority executor: USART2, priority level 7 + let irq = interrupt::take!(USART2); + irq.set_priority(interrupt::Priority::P7); + let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); + let spawner = executor.start(); + unwrap!(spawner.spawn(run_med())); + + // Low priority executor: runs in thread mode, using WFE/SEV + let executor = EXECUTOR_LOW.init(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run_low())); + }); +} From 395b5fed6497e901c4275f90afbffaf069931f84 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:55:09 +0530 Subject: [PATCH 228/374] added watchdog example --- examples/stm32f0/src/bin/wdg.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 examples/stm32f0/src/bin/wdg.rs diff --git a/examples/stm32f0/src/bin/wdg.rs b/examples/stm32f0/src/bin/wdg.rs new file mode 100644 index 00000000..80e76f90 --- /dev/null +++ b/examples/stm32f0/src/bin/wdg.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::wdg::IndependentWatchdog; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize and create handle for devicer peripherals + let p = embassy_stm32::init(Default::default()); + // Configure the independent watchdog timer + let mut wdg = IndependentWatchdog::new(p.IWDG, 20_000_00); + + info!("Watchdog start"); + unsafe { wdg.unleash() }; + + loop { + Timer::after(Duration::from_secs(1)).await; + unsafe { wdg.pet() }; + } +} From 55d9af71e3eecd188ed01dcb6684ec4baf6d5868 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:56:30 +0530 Subject: [PATCH 229/374] enabled interrupt feature --- examples/stm32f0/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index c10af171..d4afbb8f 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ panic-probe = "0.3" 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", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any"] } - +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti"] } +static_cell = "1.0" From aa92ce6dc71cf176eb6e99632fda72c875d071e6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Thu, 22 Dec 2022 23:03:05 +0100 Subject: [PATCH 230/374] embassy-rp: Add split() to BufferedUart --- embassy-rp/src/uart/buffered.rs | 7 +++ examples/rp/.cargo/config.toml | 2 +- examples/rp/src/bin/uart_buffered_split.rs | 57 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 examples/rp/src/bin/uart_buffered_split.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 32029f81..e89970d1 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -126,6 +126,13 @@ impl<'d, T: Instance> BufferedUart<'d, T> { Self { phantom: PhantomData } } + + pub fn split(&mut self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { + ( + BufferedUartRx { phantom: PhantomData }, + BufferedUartTx { phantom: PhantomData }, + ) + } } impl<'d, T: Instance> BufferedUartRx<'d, T> { diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 3d605138..7880c095 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -5,4 +5,4 @@ runner = "probe-run --chip RP2040" target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "info" diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs new file mode 100644 index 00000000..36f31c90 --- /dev/null +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_executor::_export::StaticCell; +use embassy_rp::interrupt; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let irq = interrupt::take!(UART0_IRQ); + let tx_buf = &mut singleton!([0u8; 16])[..]; + let rx_buf = &mut singleton!([0u8; 16])[..]; + let mut uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); + let (rx, mut tx) = uart.split(); + + unwrap!(spawner.spawn(reader(rx))); + + info!("Writing..."); + loop { + let data = [ + 1u8, 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, + ]; + info!("TX {:?}", data); + tx.write_all(&data).await.unwrap(); + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn reader(mut rx: BufferedUartRx<'static, UART0>) { + info!("Reading..."); + loop { + let mut buf = [0; 31]; + rx.read_exact(&mut buf).await.unwrap(); + info!("RX {:?}", buf); + } +} From da9ee837561694a7749e17d727e56da7ddb3e9b2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 09:32:18 +0100 Subject: [PATCH 231/374] fix(stm32): Fix write buffer lifetime for repeated writes --- embassy-stm32/src/dma/bdma.rs | 5 ++--- embassy-stm32/src/dma/dma.rs | 5 ++--- embassy-stm32/src/dma/gpdma.rs | 5 ++--- embassy-stm32/src/dma/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index e6ce05b7..7da22ec1 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -78,8 +78,7 @@ foreach_dma_channel! { ); } - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { - let buf = [repeated]; + unsafe fn start_write_repeated(&mut self, _request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, @@ -87,7 +86,7 @@ foreach_dma_channel! { _request, vals::Dir::FROMMEMORY, reg_addr as *const u32, - buf.as_ptr() as *mut u32, + repeated.as_ptr() as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 97a3df08..45a38dda 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -102,15 +102,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { - let buf = [repeated]; + unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, vals::Dir::MEMORYTOPERIPHERAL, reg_addr as *const u32, - buf.as_ptr() as *mut u32, + repeated.as_ptr() as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index bde8c3ef..46d8715b 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -75,15 +75,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { - let buf = [repeated]; + unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, low_level_api::Dir::MemoryToPeripheral, reg_addr as *const u32, - buf.as_ptr() as *mut u32, + repeated.as_ptr() as *mut u32, count, false, W::bits(), diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 74bce6aa..31f55b86 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -59,7 +59,7 @@ pub(crate) mod sealed { unsafe fn start_write_repeated( &mut self, request: Request, - repeated: W, + repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions, @@ -246,7 +246,7 @@ mod transfers { pub fn write_repeated<'a, W: Word>( channel: impl Peripheral

+ 'a, request: Request, - repeated: W, + repeated: &[W; 1], count: usize, reg_addr: *mut W, ) -> impl Future + 'a { From 2457fcaa35f7ceafcaee44f5e4b2a63c83674c89 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 09:33:34 +0100 Subject: [PATCH 232/374] fix(stm32): Align with updated dma::write_repeated signature --- embassy-stm32/src/spi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index ab4352a5..1d4baf46 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -473,8 +473,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - let clock_byte = 0x00u8; - let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, clock_byte, clock_byte_count, tx_dst); + let clock_byte = [0x00u8]; + let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); unsafe { set_txdmaen(T::REGS, true); From 662bb5797f7723f16b0d5211c17e6a9fdeb2cf50 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 09:34:42 +0100 Subject: [PATCH 233/374] fix(stm32): Ensure that gpio speed is VeryHigh for all spi versions This fixes #1095 --- embassy-stm32/src/spi/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 1d4baf46..439d92b9 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -95,13 +95,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { into_ref!(peri, sck, mosi, miso); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] mosi.set_speed(crate::gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); - #[cfg(any(spi_v2, spi_v3, spi_v4))] miso.set_speed(crate::gpio::Speed::VeryHigh); } @@ -129,10 +126,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { into_ref!(sck, miso); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); - #[cfg(any(spi_v2, spi_v3, spi_v4))] miso.set_speed(crate::gpio::Speed::VeryHigh); } @@ -160,10 +155,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { into_ref!(sck, mosi); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] mosi.set_speed(crate::gpio::Speed::VeryHigh); } @@ -772,10 +765,13 @@ fn finish_dma(regs: Regs) { #[cfg(not(any(spi_v3, spi_v4)))] while regs.sr().read().bsy() {} + // Disable the spi peripheral regs.cr1().modify(|w| { w.set_spe(false); }); + // The peripheral automatically disables the DMA stream on completion without error, + // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. #[cfg(not(any(spi_v3, spi_v4)))] regs.cr2().modify(|reg| { reg.set_txdmaen(false); From e9a2c4a9e38ada8a6927e6dba66d486d190edf21 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 15:40:09 +0100 Subject: [PATCH 234/374] Let start_write_repeated accept pointer instead of slice --- embassy-stm32/src/dma/bdma.rs | 4 ++-- embassy-stm32/src/dma/dma.rs | 4 ++-- embassy-stm32/src/dma/gpdma.rs | 4 ++-- embassy-stm32/src/dma/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 7da22ec1..bc51cdc4 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -78,7 +78,7 @@ foreach_dma_channel! { ); } - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, @@ -86,7 +86,7 @@ foreach_dma_channel! { _request, vals::Dir::FROMMEMORY, reg_addr as *const u32, - repeated.as_ptr() as *mut u32, + repeated as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 45a38dda..25050585 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -102,14 +102,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, vals::Dir::MEMORYTOPERIPHERAL, reg_addr as *const u32, - repeated.as_ptr() as *mut u32, + repeated as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 46d8715b..87c0dfdf 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -75,14 +75,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, low_level_api::Dir::MemoryToPeripheral, reg_addr as *const u32, - repeated.as_ptr() as *mut u32, + repeated as *mut u32, count, false, W::bits(), diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 31f55b86..706bcff3 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -59,7 +59,7 @@ pub(crate) mod sealed { unsafe fn start_write_repeated( &mut self, request: Request, - repeated: &[W; 1], + repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions, @@ -246,7 +246,7 @@ mod transfers { pub fn write_repeated<'a, W: Word>( channel: impl Peripheral

+ 'a, request: Request, - repeated: &[W; 1], + repeated: *const [W], count: usize, reg_addr: *mut W, ) -> impl Future + 'a { From 47a0769fc287afa8c982514ea34de0d4f18a3f99 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 15:49:22 +0100 Subject: [PATCH 235/374] Let repeated clock byte be singular pointer and not array pointer --- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 2 +- embassy-stm32/src/dma/mod.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index bc51cdc4..6160b9f4 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -78,7 +78,7 @@ foreach_dma_channel! { ); } - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 25050585..fec60f70 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -102,7 +102,7 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 87c0dfdf..d252cef3 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -75,7 +75,7 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 706bcff3..b51e0d40 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -59,7 +59,7 @@ pub(crate) mod sealed { unsafe fn start_write_repeated( &mut self, request: Request, - repeated: *const [W], + repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions, @@ -246,7 +246,7 @@ mod transfers { pub fn write_repeated<'a, W: Word>( channel: impl Peripheral

+ 'a, request: Request, - repeated: *const [W], + repeated: *const W, count: usize, reg_addr: *mut W, ) -> impl Future + 'a { diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 439d92b9..5b81c791 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -466,7 +466,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - let clock_byte = [0x00u8]; + let clock_byte = 0x00u8; let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); unsafe { From cd9a65ba3969460cd7ecb9200f12a828c26205aa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 20:45:51 +0100 Subject: [PATCH 236/374] stm32/usb: use separate irq flags. - Fixes race condition that could cause losing irqs (because `if flags != 0` was clearing all) - Doesn't need CAS, which is nice for thumbv6. --- embassy-stm32/src/usb/usb.rs | 66 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 460abfe2..cba4915c 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -2,10 +2,9 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::Ordering; +use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; @@ -35,10 +34,9 @@ static BUS_WAKER: AtomicWaker = NEW_AW; static EP0_SETUP: AtomicBool = AtomicBool::new(false); static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; -static IRQ_FLAGS: AtomicU8 = AtomicU8::new(0); -const IRQ_FLAG_RESET: u8 = 0x01; -const IRQ_FLAG_SUSPEND: u8 = 0x02; -const IRQ_FLAG_RESUME: u8 = 0x04; +static IRQ_RESET: AtomicBool = AtomicBool::new(false); +static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); +static IRQ_RESUME: AtomicBool = AtomicBool::new(false); fn convert_type(t: EndpointType) -> EpType { match t { @@ -184,47 +182,51 @@ impl<'d, T: Instance> Driver<'d, T> { let istr = regs.istr().read(); - let mut flags: u8 = 0; - if istr.susp() { //trace!("USB IRQ: susp"); - flags |= IRQ_FLAG_SUSPEND; + IRQ_SUSPEND.store(true, Ordering::Relaxed); regs.cntr().modify(|w| { w.set_fsusp(true); w.set_lpmode(true); - }) + }); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_susp(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); } if istr.wkup() { //trace!("USB IRQ: wkup"); - flags |= IRQ_FLAG_RESUME; + IRQ_RESUME.store(true, Ordering::Relaxed); regs.cntr().modify(|w| { w.set_fsusp(false); w.set_lpmode(false); - }) + }); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_wkup(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); } if istr.reset() { //trace!("USB IRQ: reset"); - flags |= IRQ_FLAG_RESET; + IRQ_RESET.store(true, Ordering::Relaxed); // Write 0 to clear. let mut clear = regs::Istr(!0); clear.set_reset(false); regs.istr().write_value(clear); - } - if flags != 0 { - // Send irqs to main thread. - IRQ_FLAGS.fetch_or(flags, Ordering::AcqRel); + // Wake main thread. BUS_WAKER.wake(); - - // Clear them - let mut mask = regs::Istr(0); - mask.set_wkup(true); - mask.set_susp(true); - mask.set_reset(true); - regs.istr().write_value(regs::Istr(!(istr.0 & mask.0))); } if istr.ctr() { @@ -436,17 +438,15 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { if self.inited { let regs = T::regs(); - let flags = IRQ_FLAGS.load(Ordering::Acquire); - - if flags & IRQ_FLAG_RESUME != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel); + if IRQ_RESUME.load(Ordering::Acquire) { + IRQ_RESUME.store(false, Ordering::Relaxed); return Poll::Ready(Event::Resume); } - if flags & IRQ_FLAG_RESET != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); + if IRQ_RESET.load(Ordering::Acquire) { + IRQ_RESET.store(false, Ordering::Relaxed); - trace!("RESET REGS WRITINGINGING"); + trace!("RESET"); regs.daddr().write(|w| { w.set_ef(true); w.set_add(0); @@ -475,8 +475,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { return Poll::Ready(Event::Reset); } - if flags & IRQ_FLAG_SUSPEND != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); + if IRQ_SUSPEND.load(Ordering::Acquire) { + IRQ_SUSPEND.store(false, Ordering::Relaxed); return Poll::Ready(Event::Suspend); } From 10c9cc31b14a356e58833fd6c81456251ab3fce9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 20:46:49 +0100 Subject: [PATCH 237/374] Remove unnecessary use of atomic-polyfill. Only use it when CAS is actually needed. --- .vscode/settings.json | 11 ++++++----- embassy-executor/src/arch/riscv32.rs | 3 +-- embassy-executor/src/arch/xtensa.rs | 3 +-- embassy-net/Cargo.toml | 1 - embassy-net/src/tcp.rs | 2 +- embassy-rp/src/multicore.rs | 4 +--- embassy-rp/src/usb.rs | 3 +-- embassy-stm32/src/adc/v4.rs | 3 ++- embassy-stm32/src/flash/f4.rs | 3 +-- embassy-stm32/src/flash/f7.rs | 3 +-- embassy-stm32/src/flash/h7.rs | 4 ++-- embassy-stm32/src/i2c/v2.rs | 5 +++-- embassy-stm32/src/usart/buffered.rs | 2 +- embassy-stm32/src/usart/mod.rs | 2 +- embassy-sync/Cargo.toml | 1 - 15 files changed, 22 insertions(+), 28 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 62e5a362..fd2e4afb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,8 @@ "rust-analyzer.checkOnSave.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, - "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + //"rust-analyzer.cargo.target": "thumbv7em-none-eabi", + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ // These are needed to prevent embassy-net from failing to build //"embassy-net/medium-ethernet", @@ -12,19 +13,19 @@ //"embassy-net/pool-16", //"time-tick-16mhz", //"defmt-timestamp-uptime", - "nightly", + //"nightly", //"unstable-traits", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop //"embassy-executor/Cargo.toml", //"embassy-sync/Cargo.toml", - "examples/nrf/Cargo.toml", + //"examples/nrf/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", - // "examples/stm32f1/Cargo.toml", + //"examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", @@ -35,7 +36,7 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", - // "examples/stm32l5/Cargo.toml", + "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 2a4b006d..e97a56cd 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use core::ptr; - -use atomic_polyfill::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use super::{raw, Spawner}; diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index f908aaa7..4ee0d9f7 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use core::ptr; - -use atomic_polyfill::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use super::{raw, Spawner}; diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index ac338843..2d853762 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -51,7 +51,6 @@ generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" -atomic-polyfill = "1.0.1" embedded-nal-async = { version = "0.3.0", optional = true } [dependencies.smoltcp] diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0dc8da73..55cbda45 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -329,8 +329,8 @@ pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; + use core::sync::atomic::{AtomicBool, Ordering}; - use atomic_polyfill::{AtomicBool, Ordering}; use embedded_nal_async::IpAddr; use super::*; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 8290c62a..e51fc218 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -29,9 +29,7 @@ //! ``` use core::mem::ManuallyDrop; -use core::sync::atomic::{compiler_fence, Ordering}; - -use atomic_polyfill::AtomicBool; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::peripherals::CORE1; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index dfc2e9da..8361736a 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,10 +1,9 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::slice; -use core::sync::atomic::Ordering; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index f5aa0e63..4707b7c9 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,4 +1,5 @@ -use atomic_polyfill::{AtomicU8, Ordering}; +use core::sync::atomic::{AtomicU8, Ordering}; + use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index b8327ce4..9e23a8ad 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 6d47b78a..dd0d8439 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use crate::flash::Error; use crate::pac; diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 3178b3be..7de95ac1 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -41,7 +41,7 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error cortex_m::asm::isb(); cortex_m::asm::dsb(); - atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); let ret = { let mut ret: Result<(), Error> = Ok(()); @@ -70,7 +70,7 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error cortex_m::asm::isb(); cortex_m::asm::dsb(); - atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); ret } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 47dc7d2a..4622635b 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,8 +1,8 @@ use core::cmp; use core::future::poll_fn; +use core::sync::atomic::{AtomicUsize, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicUsize, Ordering}; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -131,7 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if isr.tcr() || isr.tc() { let state = T::state(); - state.chunks_transferred.fetch_add(1, Ordering::Relaxed); + let transferred = state.chunks_transferred.load(Ordering::Relaxed); + state.chunks_transferred.store(transferred + 1, Ordering::Relaxed); state.waker.wake(); } // The flag can only be cleared by writting to nbytes, we won't do that here, so disable diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 874af1d7..a27fcc1c 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,8 +1,8 @@ use core::cell::RefCell; use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; 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; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ea75361f..6f8b6a9e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -2,9 +2,9 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; use embassy_futures::select::{select, Either}; use embassy_hal_common::drop::OnDrop; diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 1eeb94c9..7b5d3ce4 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -31,7 +31,6 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } futures-util = { version = "0.3.17", default-features = false } -atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" From 787745188c3b62a0170d15ba39f3e58929ae203b Mon Sep 17 00:00:00 2001 From: kalkyl Date: Fri, 23 Dec 2022 23:14:58 +0100 Subject: [PATCH 238/374] Change log level to debug --- examples/rp/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 7880c095..d1c8c1c5 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -5,4 +5,4 @@ runner = "probe-run --chip RP2040" target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ [env] -DEFMT_LOG = "info" +DEFMT_LOG = "debug" From eaad0cc1dc09e604d137b3a1bdfd3438ff990621 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 24 Dec 2022 02:51:06 +0100 Subject: [PATCH 239/374] embassy-rp: Add Watchdog --- embassy-rp/src/lib.rs | 3 + embassy-rp/src/watchdog.rs | 111 ++++++++++++++++++++++++++++++++ examples/rp/src/bin/watchdog.rs | 48 ++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 embassy-rp/src/watchdog.rs create mode 100644 examples/rp/src/bin/watchdog.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 0ea97aec..9e99b2fb 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -36,6 +36,7 @@ pub mod clocks; pub mod flash; pub mod multicore; mod reset; +pub mod watchdog; // Reexports @@ -119,6 +120,8 @@ embassy_hal_common::peripherals! { PIO0, PIO1, + + WATCHDOG, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs new file mode 100644 index 00000000..01509ff4 --- /dev/null +++ b/embassy-rp/src/watchdog.rs @@ -0,0 +1,111 @@ +//! Watchdog +//! +//! The watchdog is a countdown timer that can restart parts of the chip if it reaches zero. This can be used to restart the +//! processor if software gets stuck in an infinite loop. The programmer must periodically write a value to the watchdog to +//! stop it from reaching zero. +//! +//! Credit: based on `rp-hal` implementation (also licensed Apache+MIT) + +use core::marker::PhantomData; + +use embassy_time::Duration; + +use crate::pac; +use crate::peripherals::WATCHDOG; + +/// Watchdog peripheral +pub struct Watchdog<'d> { + phantom: PhantomData<&'d WATCHDOG>, + load_value: u32, // decremented by 2 per tick (µs) +} + +impl<'d> Watchdog<'d> { + /// Create a new `Watchdog` + pub fn new(_watchdog: WATCHDOG) -> Self { + Self { + phantom: PhantomData, + load_value: 0, + } + } + + /// Start tick generation on clk_tick which is driven from clk_ref. + /// + /// # Arguments + /// + /// * `cycles` - Total number of tick cycles before the next tick is generated. + /// It is expected to be the frequency in MHz of clk_ref. + pub fn enable_tick_generation(&mut self, cycles: u8) { + const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200; + unsafe { + let watchdog = pac::WATCHDOG; + watchdog + .tick() + .write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32)) + } + } + + /// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode + /// or when JTAG is accessing bus fabric + pub fn pause_on_debug(&mut self, pause: bool) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| { + w.set_pause_dbg0(pause); + w.set_pause_dbg1(pause); + w.set_pause_jtag(pause); + }) + } + } + + fn load_counter(&self, counter: u32) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.load().write_value(pac::watchdog::regs::Load(counter)); + } + } + + fn enable(&self, bit: bool) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| w.set_enable(bit)) + } + } + + // Configure which hardware will be reset by the watchdog + // (everything except ROSC, XOSC) + unsafe fn configure_wdog_reset_triggers(&self) { + let psm = pac::PSM; + psm.wdsel().write_value(pac::psm::regs::Wdsel( + 0x0001ffff & !(0x01 << 0usize) & !(0x01 << 1usize), + )); + } + + /// Feed the watchdog timer + pub fn feed(&mut self) { + self.load_counter(self.load_value) + } + + /// Start the watchdog timer + pub fn start(&mut self, period: Duration) { + const MAX_PERIOD: u32 = 0xFFFFFF; + + let delay_us = period.as_micros() as u32; + if delay_us > MAX_PERIOD / 2 { + panic!( + "Period cannot exceed maximum load value of {} ({} microseconds))", + MAX_PERIOD, + MAX_PERIOD / 2 + ); + } + // Due to a logic error, the watchdog decrements by 2 and + // the load value must be compensated; see RP2040-E1 + self.load_value = delay_us * 2; + + self.enable(false); + unsafe { + self.configure_wdog_reset_triggers(); + } + self.load_counter(self.load_value); + self.enable(true); + } +} diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs new file mode 100644 index 00000000..13af22a2 --- /dev/null +++ b/examples/rp/src/bin/watchdog.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::gpio; +use embassy_rp::watchdog::*; +use embassy_time::{Duration, Timer}; +use gpio::{Level, Output}; +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 mut watchdog = Watchdog::new(p.WATCHDOG); + let mut led = Output::new(p.PIN_25, Level::Low); + + // Set the LED high for 2 seconds so we know when we're about to start the watchdog + led.set_high(); + Timer::after(Duration::from_secs(2)).await; + + // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it + watchdog.start(Duration::from_millis(1_050)); + info!("Started the watchdog timer"); + + // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset + for _ in 1..=5 { + led.set_low(); + Timer::after(Duration::from_millis(500)).await; + led.set_high(); + Timer::after(Duration::from_millis(500)).await; + info!("Feeding watchdog"); + watchdog.feed(); + } + + info!("Stopped feeding, device will reset in 1.05 seconds"); + // Blink 10 times per second, not feeding the watchdog. + // The processor should reset in 1.05 seconds, or 5 blinks time + loop { + led.set_low(); + Timer::after(Duration::from_millis(100)).await; + led.set_high(); + Timer::after(Duration::from_millis(100)).await; + } +} From e090ab19151fbea6736d5eac0e1497ef9c36626b Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 24 Dec 2022 03:22:51 +0100 Subject: [PATCH 240/374] Remove lifetime, use pac fields --- embassy-rp/src/watchdog.rs | 14 +++++++------- examples/rp/src/bin/watchdog.rs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 01509ff4..f4f165b2 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -14,12 +14,12 @@ use crate::pac; use crate::peripherals::WATCHDOG; /// Watchdog peripheral -pub struct Watchdog<'d> { - phantom: PhantomData<&'d WATCHDOG>, +pub struct Watchdog { + phantom: PhantomData, load_value: u32, // decremented by 2 per tick (µs) } -impl<'d> Watchdog<'d> { +impl Watchdog { /// Create a new `Watchdog` pub fn new(_watchdog: WATCHDOG) -> Self { Self { @@ -35,12 +35,12 @@ impl<'d> Watchdog<'d> { /// * `cycles` - Total number of tick cycles before the next tick is generated. /// It is expected to be the frequency in MHz of clk_ref. pub fn enable_tick_generation(&mut self, cycles: u8) { - const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200; unsafe { let watchdog = pac::WATCHDOG; - watchdog - .tick() - .write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32)) + watchdog.tick().write(|w| { + w.set_enable(true); + w.set_cycles(cycles.into()) + }); } } diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs index 13af22a2..ece5cfe3 100644 --- a/examples/rp/src/bin/watchdog.rs +++ b/examples/rp/src/bin/watchdog.rs @@ -22,11 +22,11 @@ async fn main(_spawner: Spawner) { led.set_high(); Timer::after(Duration::from_secs(2)).await; - // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it + // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it watchdog.start(Duration::from_millis(1_050)); info!("Started the watchdog timer"); - // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset + // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset for _ in 1..=5 { led.set_low(); Timer::after(Duration::from_millis(500)).await; @@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) { info!("Stopped feeding, device will reset in 1.05 seconds"); // Blink 10 times per second, not feeding the watchdog. - // The processor should reset in 1.05 seconds, or 5 blinks time + // The processor should reset in 1.05 seconds. loop { led.set_low(); Timer::after(Duration::from_millis(100)).await; From 056eac998a5668c6737aafb44edc5bbe23e2df46 Mon Sep 17 00:00:00 2001 From: Brendon Fallquist Date: Sat, 24 Dec 2022 21:21:07 -0600 Subject: [PATCH 241/374] Hide doc comments from inner function include doc comments on outer function --- embassy-macros/src/macros/task.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 573776f8..90d2cd89 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs @@ -1,6 +1,7 @@ use darling::FromMeta; use proc_macro2::TokenStream; use quote::{format_ident, quote}; +use syn::{parse_quote, ItemFn}; use crate::util::ctxt::Ctxt; @@ -57,13 +58,7 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { type Fut = impl ::core::future::Future + 'static; static POOL: ::embassy_executor::raw::TaskPool = ::embassy_executor::raw::TaskPool::new(); @@ -71,5 +66,18 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result Date: Sun, 25 Dec 2022 23:49:04 +0000 Subject: [PATCH 242/374] Add missing SPI pins --- embassy-rp/src/spi.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 2b7a818d..584370d5 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -473,6 +473,16 @@ impl_pin!(PIN_16, SPI0, MisoPin); impl_pin!(PIN_17, SPI0, CsPin); impl_pin!(PIN_18, SPI0, ClkPin); impl_pin!(PIN_19, SPI0, MosiPin); +impl_pin!(PIN_20, SPI0, MisoPin); +impl_pin!(PIN_21, SPI0, CsPin); +impl_pin!(PIN_22, SPI0, ClkPin); +impl_pin!(PIN_23, SPI0, MosiPin); +impl_pin!(PIN_24, SPI1, MisoPin); +impl_pin!(PIN_25, SPI1, CsPin); +impl_pin!(PIN_26, SPI1, ClkPin); +impl_pin!(PIN_27, SPI1, MosiPin); +impl_pin!(PIN_28, SPI1, MisoPin); +impl_pin!(PIN_29, SPI1, CsPin); macro_rules! impl_mode { ($name:ident) => { From 72bb9b53a239464500ad9dc5db462bcb7917d53a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 02:42:36 +0100 Subject: [PATCH 243/374] net: remove unused pool-x features --- .vscode/settings.json | 1 - ci.sh | 8 ++++---- ci_stable.sh | 4 ++-- embassy-net/Cargo.toml | 9 +-------- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 10 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index fd2e4afb..6b8183f8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,6 @@ // These are needed to prevent embassy-net from failing to build //"embassy-net/medium-ethernet", //"embassy-net/tcp", - //"embassy-net/pool-16", //"time-tick-16mhz", //"defmt-timestamp-uptime", //"nightly", diff --git a/ci.sh b/ci.sh index 9f79c87b..14a48454 100755 --- a/ci.sh +++ b/ci.sh @@ -38,10 +38,10 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ diff --git a/ci_stable.sh b/ci_stable.sh index 0332c3fa..a1d507d7 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,8 +13,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2d853762..1faf603b 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] +features = ["defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] target = "thumbv7em-none-eabi" [features] @@ -28,13 +28,6 @@ proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] -pool-4 = [] -pool-8 = [] -pool-16 = [] -pool-32 = [] -pool-64 = [] -pool-128 = [] - [dependencies] defmt = { version = "0.3", optional = true } diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a980a505..54a477b4 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -15,7 +15,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", "defmt-timestamp-uptime"] } 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-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 31a08cfb..57cddd43 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,7 +11,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", "pio", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 41680f8f..649e3974 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index afdf8700..a8b2baf2 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -9,7 +9,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", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 5acf0035..63e6caa7 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,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", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index bb851531..ce999f67 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -12,7 +12,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", "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-net"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" From 5655c6093f8938c5ba637785b8bd572345f4c42e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 02:42:54 +0100 Subject: [PATCH 244/374] net: use atomic-polyfill on tcp client pool, for thumbv6m support. --- embassy-net/Cargo.toml | 1 + embassy-net/src/tcp.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 1faf603b..357f8792 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -45,6 +45,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" embedded-nal-async = { version = "0.3.0", optional = true } +atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 55cbda45..0dc8da73 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -329,8 +329,8 @@ pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; - use core::sync::atomic::{AtomicBool, Ordering}; + use atomic_polyfill::{AtomicBool, Ordering}; use embedded_nal_async::IpAddr; use super::*; From 639b3f1d5b4b2897b326edc52f66f18caaa3bd3e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 02:56:41 +0100 Subject: [PATCH 245/374] usb-driver: remove unused `log` feature. --- embassy-usb-driver/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index d22bf7d7..d658f9ec 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -14,4 +14,3 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } From 1f033d509afb4e590a81896de66af683fda4e706 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 03:33:49 +0100 Subject: [PATCH 246/374] net: split driver trait to a separate crate. --- .github/workflows/doc.yml | 1 + .vscode/settings.json | 22 +-- embassy-net-driver/Cargo.toml | 15 ++ embassy-net-driver/src/lib.rs | 175 +++++++++++++++++++ embassy-net/Cargo.toml | 3 +- embassy-net/src/device.rs | 117 ++++--------- embassy-net/src/lib.rs | 17 +- embassy-net/src/tcp.rs | 12 +- embassy-net/src/udp.rs | 5 +- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/eth/mod.rs | 147 ++++++++-------- embassy-stm32/src/eth/v1/mod.rs | 4 +- embassy-usb/Cargo.toml | 2 +- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 14 +- embassy-usb/src/class/cdc_ncm/mod.rs | 1 - examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 1 + examples/std/src/tuntap.rs | 12 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 22 files changed, 351 insertions(+), 211 deletions(-) create mode 100644 embassy-net-driver/Cargo.toml create mode 100644 embassy-net-driver/src/lib.rs diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index eb460e73..8a341b8f 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -69,6 +69,7 @@ jobs: builder ./embassy-futures crates/embassy-futures/git.zup builder ./embassy-lora crates/embassy-lora/git.zup builder ./embassy-net crates/embassy-net/git.zup + builder ./embassy-net-driver crates/embassy-net-driver/git.zup builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b8183f8..086f435d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,27 +4,21 @@ "rust-analyzer.checkOnSave.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, - //"rust-analyzer.cargo.target": "thumbv7em-none-eabi", - "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - // These are needed to prevent embassy-net from failing to build - //"embassy-net/medium-ethernet", - //"embassy-net/tcp", - //"time-tick-16mhz", - //"defmt-timestamp-uptime", - //"nightly", - //"unstable-traits", + "nightly", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop - //"embassy-executor/Cargo.toml", - //"embassy-sync/Cargo.toml", - //"examples/nrf/Cargo.toml", + // "embassy-executor/Cargo.toml", + // "embassy-sync/Cargo.toml", + "examples/nrf/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", - //"examples/stm32f1/Cargo.toml", + // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", @@ -35,7 +29,7 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", - "examples/stm32l5/Cargo.toml", + // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml new file mode 100644 index 00000000..7ab9d119 --- /dev/null +++ b/embassy-net-driver/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embassy-net-driver" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + +[dependencies] +defmt = { version = "0.3", optional = true } \ No newline at end of file diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs new file mode 100644 index 00000000..a39cfecc --- /dev/null +++ b/embassy-net-driver/src/lib.rs @@ -0,0 +1,175 @@ +#![no_std] + +use core::task::Context; + +pub trait Driver { + type RxToken<'a>: RxToken + where + Self: 'a; + type TxToken<'a>: TxToken + where + Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + fn transmit(&mut self, cx: &mut Context) -> Option>; + fn link_state(&mut self, cx: &mut Context) -> LinkState; + + fn capabilities(&self) -> Capabilities; + fn ethernet_address(&self) -> [u8; 6]; +} + +impl Driver for &mut T { + type RxToken<'a> = T::RxToken<'a> + where + Self: 'a; + type TxToken<'a> = T::TxToken<'a> + where + Self: 'a; + + fn transmit(&mut self, cx: &mut Context) -> Option> { + T::transmit(self, cx) + } + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + T::receive(self, cx) + } + fn capabilities(&self) -> Capabilities { + T::capabilities(self) + } + fn link_state(&mut self, cx: &mut Context) -> LinkState { + T::link_state(self, cx) + } + fn ethernet_address(&self) -> [u8; 6] { + T::ethernet_address(self) + } +} + +/// A token to receive a single network packet. +pub trait RxToken { + /// Consumes the token to receive a single network packet. + /// + /// This method receives a packet and then calls the given closure `f` with the raw + /// packet bytes as argument. + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; +} + +/// A token to transmit a single network packet. +pub trait TxToken { + /// Consumes the token to send a single network packet. + /// + /// This method constructs a transmit buffer of size `len` and calls the passed + /// closure `f` with a mutable reference to that buffer. The closure should construct + /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure + /// returns, the transmit buffer is sent out. + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; +} + +/// A description of device capabilities. +/// +/// Higher-level protocols may achieve higher throughput or lower latency if they consider +/// the bandwidth or packet size limitations. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct Capabilities { + /// Medium of the device. + /// + /// This indicates what kind of packet the sent/received bytes are, and determines + /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done + /// for Ethernet mediums. + pub medium: Medium, + + /// Maximum transmission unit. + /// + /// The network device is unable to send or receive frames larger than the value returned + /// by this function. + /// + /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but + /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14. + /// + /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet + /// devices. This is a common source of confusion. + /// + /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets. + pub max_transmission_unit: usize, + + /// Maximum burst size, in terms of MTU. + /// + /// The network device is unable to send or receive bursts large than the value returned + /// by this function. + /// + /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are + /// dynamically allocated. + pub max_burst_size: Option, + + /// Checksum behavior. + /// + /// If the network device is capable of verifying or computing checksums for some protocols, + /// it can request that the stack not do so in software to improve performance. + pub checksum: ChecksumCapabilities, +} + +/// Type of medium of a device. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Medium { + /// Ethernet medium. Devices of this type send and receive Ethernet frames, + /// and interfaces using it must do neighbor discovery via ARP or NDISC. + /// + /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode. + Ethernet, + + /// IP medium. Devices of this type send and receive IP frames, without an + /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done. + /// + /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. + Ip, +} + +impl Default for Medium { + fn default() -> Medium { + Medium::Ethernet + } +} + +/// A description of checksum behavior for every supported protocol. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChecksumCapabilities { + pub ipv4: Checksum, + pub udp: Checksum, + pub tcp: Checksum, + pub icmpv4: Checksum, + pub icmpv6: Checksum, +} + +/// A description of checksum behavior for a particular protocol. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Checksum { + /// Verify checksum when receiving and compute checksum when sending. + Both, + /// Verify checksum when receiving. + Rx, + /// Compute checksum before sending. + Tx, + /// Ignore checksum completely. + None, +} + +impl Default for Checksum { + fn default() -> Checksum { + Checksum::Both + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LinkState { + Down, + Up, +} diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 357f8792..9214fd17 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -15,7 +15,7 @@ target = "thumbv7em-none-eabi" default = [] std = [] -defmt = ["dep:defmt", "smoltcp/defmt"] +defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] unstable-traits = [] @@ -33,6 +33,7 @@ medium-ip = ["smoltcp/medium-ip"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 5d86ca91..44f7dc7b 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -1,93 +1,20 @@ use core::task::Context; +use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken}; use smoltcp::phy; -pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium}; -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum LinkState { - Down, - Up, -} - -pub trait Device { - type RxToken<'a>: RxToken - where - Self: 'a; - type TxToken<'a>: TxToken - where - Self: 'a; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; - fn transmit(&mut self, cx: &mut Context) -> Option>; - fn link_state(&mut self, cx: &mut Context) -> LinkState; - - fn capabilities(&self) -> phy::DeviceCapabilities; - fn ethernet_address(&self) -> [u8; 6]; -} - -impl Device for &mut T { - type RxToken<'a> = T::RxToken<'a> - where - Self: 'a; - type TxToken<'a> = T::TxToken<'a> - where - Self: 'a; - - fn transmit(&mut self, cx: &mut Context) -> Option> { - T::transmit(self, cx) - } - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - T::receive(self, cx) - } - fn capabilities(&self) -> phy::DeviceCapabilities { - T::capabilities(self) - } - fn link_state(&mut self, cx: &mut Context) -> LinkState { - T::link_state(self, cx) - } - fn ethernet_address(&self) -> [u8; 6] { - T::ethernet_address(self) - } -} - -/// A token to receive a single network packet. -pub trait RxToken { - /// Consumes the token to receive a single network packet. - /// - /// This method receives a packet and then calls the given closure `f` with the raw - /// packet bytes as argument. - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R; -} - -/// A token to transmit a single network packet. -pub trait TxToken { - /// Consumes the token to send a single network packet. - /// - /// This method constructs a transmit buffer of size `len` and calls the passed - /// closure `f` with a mutable reference to that buffer. The closure should construct - /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure - /// returns, the transmit buffer is sent out. - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R; -} - -/////////////////////////// - -pub(crate) struct DeviceAdapter<'d, 'c, T> +pub(crate) struct DriverAdapter<'d, 'c, T> where - T: Device, + T: Driver, { // must be Some when actually using this to rx/tx pub cx: Option<&'d mut Context<'c>>, pub inner: &'d mut T, } -impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> +impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> where - T: Device, + T: Driver, { type RxToken<'a> = RxTokenAdapter> where Self: 'a; type TxToken<'a> = TxTokenAdapter> where Self: 'a; @@ -105,7 +32,39 @@ where /// Get a description of device capabilities. fn capabilities(&self) -> phy::DeviceCapabilities { - self.inner.capabilities() + fn convert(c: Checksum) -> phy::Checksum { + match c { + Checksum::Both => phy::Checksum::Both, + Checksum::Tx => phy::Checksum::Tx, + Checksum::Rx => phy::Checksum::Rx, + Checksum::None => phy::Checksum::None, + } + } + let caps: Capabilities = self.inner.capabilities(); + let mut smolcaps = phy::DeviceCapabilities::default(); + + smolcaps.max_transmission_unit = caps.max_transmission_unit; + smolcaps.max_burst_size = caps.max_burst_size; + smolcaps.medium = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => phy::Medium::Ethernet, + #[cfg(feature = "medium-ip")] + Medium::Ip => phy::Medium::Ip, + _ => panic!( + "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.", + caps.medium + ), + }; + smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); + #[cfg(feature = "proto-ipv6")] + { + smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6); + } + smolcaps.checksum.tcp = convert(caps.checksum.tcp); + smolcaps.checksum.udp = convert(caps.checksum.udp); + smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + + smolcaps } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index afe0d6da..b58c9cf3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -18,6 +18,7 @@ use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; +use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; @@ -27,8 +28,6 @@ use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "medium-ethernet")] use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::phy::Medium; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; // smoltcp reexports @@ -41,7 +40,7 @@ pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; #[cfg(feature = "udp")] pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; -use crate::device::{Device, DeviceAdapter, LinkState}; +use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; @@ -82,12 +81,12 @@ pub enum ConfigStrategy { Dhcp, } -pub struct Stack { +pub struct Stack { pub(crate) socket: RefCell, inner: RefCell>, } -struct Inner { +struct Inner { device: D, link_up: bool, config: Option, @@ -102,7 +101,7 @@ pub(crate) struct SocketStack { next_local_port: u16, } -impl Stack { +impl Stack { pub fn new( mut device: D, config: ConfigStrategy, @@ -130,7 +129,7 @@ impl Stack { b = b.routes(Routes::new(&mut resources.routes[..])); } - let iface = b.finalize(&mut DeviceAdapter { + let iface = b.finalize(&mut DriverAdapter { inner: &mut device, cx: None, }); @@ -211,7 +210,7 @@ impl SocketStack { } } -impl Inner { +impl Inner { fn apply_config(&mut self, s: &mut SocketStack, config: Config) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -263,7 +262,7 @@ impl Inner { s.waker.register(cx.waker()); let timestamp = instant_to_smoltcp(Instant::now()); - let mut smoldev = DeviceAdapter { + let mut smoldev = DriverAdapter { cx: Some(cx), inner: &mut self.device, }; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0dc8da73..0fbf0c91 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -3,12 +3,12 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::device::Device; use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -66,7 +66,7 @@ impl<'a> TcpWriter<'a> { } impl<'a> TcpSocket<'a> { - pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { + pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { let s = &mut *stack.socket.borrow_mut(); let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; @@ -336,19 +336,19 @@ pub mod client { use super::*; /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. - pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { + pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { stack: &'d Stack, state: &'d TcpClientState, } - impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { + impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { /// Create a new TcpClient pub fn new(stack: &'d Stack, state: &'d TcpClientState) -> Self { Self { stack, state } } } - impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect + impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect for TcpClient<'d, D, N, TX_SZ, RX_SZ> { type Error = Error; @@ -386,7 +386,7 @@ pub mod client { } impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> { - fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { + fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; Ok(Self { socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) }, diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 2f5334df..0ee8c6e1 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -3,11 +3,12 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::{Device, SocketStack, Stack}; +use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -31,7 +32,7 @@ pub struct UdpSocket<'a> { } impl<'a> UdpSocket<'a> { - pub fn new( + pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0c491ee4..67996cca 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -39,7 +39,7 @@ 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" } -embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } 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"] } @@ -75,7 +75,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", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] sdmmc-rs = ["embedded-sdmmc"] memory-x = ["stm32-metapac/memory-x"] subghz = [] diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index fd1b4853..9f62b61e 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -1,14 +1,17 @@ #![macro_use] -#![cfg_attr(not(feature = "embassy-net"), allow(unused))] #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; pub mod generic_smi; -pub use _version::*; +use core::task::Context; + +use embassy_net_driver::{Capabilities, LinkState}; use embassy_sync::waitqueue::AtomicWaker; +pub use self::_version::*; + #[allow(unused)] const MTU: usize = 1514; const TX_BUFFER_SIZE: usize = 1514; @@ -40,92 +43,84 @@ impl PacketQueue { static WAKER: AtomicWaker = AtomicWaker::new(); -#[cfg(feature = "embassy-net")] -mod embassy_net_impl { - use core::task::Context; +impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> { + type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - use embassy_net::device::{Device, DeviceCapabilities, LinkState}; - - use super::*; - - impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { - type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; - type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - WAKER.register(cx.waker()); - if self.rx.available().is_some() && self.tx.available().is_some() { - Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) - } else { - None - } - } - - fn transmit(&mut self, cx: &mut Context) -> Option> { - WAKER.register(cx.waker()); - if self.tx.available().is_some() { - Some(TxToken { tx: &mut self.tx }) - } else { - None - } - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(self.tx.len()); - caps - } - - fn link_state(&mut self, cx: &mut Context) -> LinkState { - // TODO: wake cx.waker on link state change - cx.waker().wake_by_ref(); - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + WAKER.register(cx.waker()); + if self.rx.available().is_some() && self.tx.available().is_some() { + Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + } else { + None } } - pub struct RxToken<'a, 'd> { - rx: &'a mut RDesRing<'d>, - } - - impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.available()); - let r = f(pkt); - self.rx.pop_packet(); - r + fn transmit(&mut self, cx: &mut Context) -> Option> { + WAKER.register(cx.waker()); + if self.tx.available().is_some() { + Some(TxToken { tx: &mut self.tx }) + } else { + None } } - pub struct TxToken<'a, 'd> { - tx: &'a mut TDesRing<'d>, + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(self.tx.len()); + caps } - impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.tx.available()); - let r = f(&mut pkt[..len]); - self.tx.transmit(len); - r + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // TODO: wake cx.waker on link state change + cx.waker().wake_by_ref(); + if P::poll_link(self) { + LinkState::Up + } else { + LinkState::Down } } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } } + +pub struct RxToken<'a, 'd> { + rx: &'a mut RDesRing<'d>, +} + +impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.available()); + let r = f(pkt); + self.rx.pop_packet(); + r + } +} + +pub struct TxToken<'a, 'd> { + tx: &'a mut TDesRing<'d>, +} + +impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.available()); + let r = f(&mut pkt[..len]); + self.tx.transmit(len); + r + } +} + /// Station Management Interface (SMI) on an ethernet PHY /// /// # Safety diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index de36d3da..9c0f4d66 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -13,7 +13,7 @@ pub(crate) use self::rx_desc::{RDes, RDesRing}; pub(crate) use self::tx_desc::{TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; -use crate::gpio::{AnyPin, Speed}; +use crate::gpio::AnyPin; #[cfg(eth_v1a)] use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] @@ -66,7 +66,7 @@ macro_rules! config_pins { critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); - $pin.set_speed(Speed::VeryHigh); + $pin.set_speed(crate::gpio::Speed::VeryHigh); )* }) }; diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1e72ce68..d0742ceb 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,7 +19,7 @@ default = ["usbd-hid"] 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" } -embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 60bbfd8d..093afeff 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -3,7 +3,7 @@ use core::mem::MaybeUninit; use core::task::Context; use embassy_futures::select::{select, Either}; -use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; +use embassy_net_driver::{Capabilities, LinkState, Medium}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_sync::waitqueue::WakerRegistration; @@ -108,7 +108,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - let mut caps = DeviceCapabilities::default(); + let mut caps = Capabilities::default(); caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header caps.medium = Medium::Ethernet; @@ -158,11 +158,11 @@ pub struct Device<'d, const MTU: usize> { rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, link_state: &'d Mutex>, - caps: DeviceCapabilities, + caps: Capabilities, ethernet_address: [u8; 6], } -impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { +impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; @@ -184,7 +184,7 @@ impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { } /// Get a description of device capabilities. - fn capabilities(&self) -> DeviceCapabilities { + fn capabilities(&self) -> Capabilities { self.caps.clone() } @@ -205,7 +205,7 @@ pub struct RxToken<'a, const MTU: usize> { rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, } -impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { +impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -222,7 +222,7 @@ pub struct TxToken<'a, const MTU: usize> { tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, } -impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { +impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { fn consume(mut self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 2ee47f68..4954a65b 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -21,7 +21,6 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; use crate::Builder; -#[cfg(feature = "embassy-net")] pub mod embassy_net; /// This should be used as `device_class` when building the `UsbDevice`. diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 54a477b4..994823a9 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -16,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57cddd43..afd1042a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,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", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 649e3974..45b2a4a4 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,6 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] } +embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index bb3e194c..d918a2e6 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; -use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; +use embassy_net_driver::{self, Capabilities, Driver, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -137,7 +137,7 @@ impl TunTapDevice { } } -impl Device for TunTapDevice { +impl Driver for TunTapDevice { type RxToken<'a> = RxToken where Self: 'a; type TxToken<'a> = TxToken<'a> where Self: 'a; @@ -170,8 +170,8 @@ impl Device for TunTapDevice { }) } - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); caps.max_transmission_unit = self.device.get_ref().mtu; caps } @@ -190,7 +190,7 @@ pub struct RxToken { buffer: Vec, } -impl device::RxToken for RxToken { +impl embassy_net_driver::RxToken for RxToken { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -204,7 +204,7 @@ pub struct TxToken<'a> { device: &'a mut Async, } -impl<'a> device::TxToken for TxToken<'a> { +impl<'a> embassy_net_driver::TxToken for TxToken<'a> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a8b2baf2..b80dbbf9 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.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", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 63e6caa7..d30c42b1 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.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", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index ce999f67..c0accb0d 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,7 +11,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", "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-net"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" From 007246b16036c3aa874e78d0665567a27ab35fa9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 04:31:49 +0100 Subject: [PATCH 247/374] net: split channel-based driver impl from usb cdc-ncm into a separate crate. --- embassy-net-driver-channel/Cargo.toml | 12 + embassy-net-driver-channel/src/fmt.rs | 225 ++++++++ embassy-net-driver-channel/src/lib.rs | 525 +++++++++++++++++++ embassy-usb/Cargo.toml | 2 +- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 408 +------------- 5 files changed, 787 insertions(+), 385 deletions(-) create mode 100644 embassy-net-driver-channel/Cargo.toml create mode 100644 embassy-net-driver-channel/src/fmt.rs create mode 100644 embassy-net-driver-channel/src/lib.rs diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml new file mode 100644 index 00000000..700a4e8a --- /dev/null +++ b/embassy-net-driver-channel/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "embassy-net-driver-channel" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } diff --git a/embassy-net-driver-channel/src/fmt.rs b/embassy-net-driver-channel/src/fmt.rs new file mode 100644 index 00000000..06697081 --- /dev/null +++ b/embassy-net-driver-channel/src/fmt.rs @@ -0,0 +1,225 @@ +#![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-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs new file mode 100644 index 00000000..369dc5a9 --- /dev/null +++ b/embassy-net-driver-channel/src/lib.rs @@ -0,0 +1,525 @@ +#![no_std] + +// must go first! +mod fmt; + +use core::cell::RefCell; +use core::mem::MaybeUninit; +use core::task::{Context, Poll}; + +pub use embassy_net_driver as driver; +use embassy_net_driver::{Capabilities, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::waitqueue::WakerRegistration; + +pub struct State { + rx: [PacketBuf; N_RX], + tx: [PacketBuf; N_TX], + inner: MaybeUninit>, +} + +impl State { + const NEW_PACKET: PacketBuf = PacketBuf::new(); + + pub const fn new() -> Self { + Self { + rx: [Self::NEW_PACKET; N_RX], + tx: [Self::NEW_PACKET; N_TX], + inner: MaybeUninit::uninit(), + } + } +} + +struct StateInner<'d, const MTU: usize> { + rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + link_state: Mutex>, +} + +/// State of the LinkState +struct LinkStateState { + state: LinkState, + waker: WakerRegistration, +} + +pub struct Runner<'d, const MTU: usize> { + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +pub struct RxRunner<'d, const MTU: usize> { + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +pub struct TxRunner<'d, const MTU: usize> { + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, +} + +impl<'d, const MTU: usize> Runner<'d, MTU> { + pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { + ( + RxRunner { + link_state: self.link_state, + rx_chan: self.rx_chan, + }, + TxRunner { tx_chan: self.tx_chan }, + ) + } + + pub fn set_link_state(&mut self, state: LinkState) { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = state; + s.waker.wake(); + }); + } + + pub async fn rx_buf(&mut self) -> &mut [u8] { + let p = self.rx_chan.send().await; + &mut p.buf + } + + pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.rx_chan.try_send()?; + Some(&mut p.buf) + } + + pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.rx_chan.poll_send(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf), + Poll::Pending => Poll::Pending, + } + } + + pub fn rx_done(&mut self, len: usize) { + let p = self.rx_chan.try_send().unwrap(); + p.len = len; + self.rx_chan.send_done(); + } + + pub async fn tx_buf(&mut self) -> &mut [u8] { + let p = self.tx_chan.recv().await; + &mut p.buf[..p.len] + } + + pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.tx_chan.try_recv()?; + Some(&mut p.buf[..p.len]) + } + + pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.tx_chan.poll_recv(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), + Poll::Pending => Poll::Pending, + } + } + + pub fn tx_done(&mut self) { + self.tx_chan.recv_done(); + } +} + +impl<'d, const MTU: usize> RxRunner<'d, MTU> { + pub fn set_link_state(&mut self, state: LinkState) { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = state; + s.waker.wake(); + }); + } + + pub async fn rx_buf(&mut self) -> &mut [u8] { + let p = self.rx_chan.send().await; + &mut p.buf + } + + pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.rx_chan.try_send()?; + Some(&mut p.buf) + } + + pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.rx_chan.poll_send(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf), + Poll::Pending => Poll::Pending, + } + } + + pub fn rx_done(&mut self, len: usize) { + let p = self.rx_chan.try_send().unwrap(); + p.len = len; + self.rx_chan.send_done(); + } +} + +impl<'d, const MTU: usize> TxRunner<'d, MTU> { + pub async fn tx_buf(&mut self) -> &mut [u8] { + let p = self.tx_chan.recv().await; + &mut p.buf[..p.len] + } + + pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.tx_chan.try_recv()?; + Some(&mut p.buf[..p.len]) + } + + pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.tx_chan.poll_recv(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), + Poll::Pending => Poll::Pending, + } + } + + pub fn tx_done(&mut self) { + self.tx_chan.recv_done(); + } +} + +pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( + state: &'d mut State, + ethernet_address: [u8; 6], +) -> (Runner<'d, MTU>, Device<'d, MTU>) { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.medium = Medium::Ethernet; + + // safety: this is a self-referential struct, however: + // - it can't move while the `'d` borrow is active. + // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again. + let state_uninit: *mut MaybeUninit> = + (&mut state.inner as *mut MaybeUninit>).cast(); + let state = unsafe { &mut *state_uninit }.write(StateInner { + rx: zerocopy_channel::Channel::new(&mut state.rx[..]), + tx: zerocopy_channel::Channel::new(&mut state.tx[..]), + link_state: Mutex::new(RefCell::new(LinkStateState { + state: LinkState::Down, + waker: WakerRegistration::new(), + })), + }); + + let (rx_sender, rx_receiver) = state.rx.split(); + let (tx_sender, tx_receiver) = state.tx.split(); + + ( + Runner { + tx_chan: tx_receiver, + rx_chan: rx_sender, + link_state: &state.link_state, + }, + Device { + caps, + ethernet_address, + link_state: &state.link_state, + rx: rx_receiver, + tx: tx_sender, + }, + ) +} + +pub struct PacketBuf { + len: usize, + buf: [u8; MTU], +} + +impl PacketBuf { + pub const fn new() -> Self { + Self { len: 0, buf: [0; MTU] } + } +} + +pub struct Device<'d, const MTU: usize> { + rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, + caps: Capabilities, + ethernet_address: [u8; 6], +} + +impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { + type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; + type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { + Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) + } else { + None + } + } + + /// Construct a transmit token. + fn transmit(&mut self, cx: &mut Context) -> Option> { + if self.tx.poll_send(cx).is_ready() { + Some(TxToken { tx: self.tx.borrow() }) + } else { + None + } + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> Capabilities { + self.caps.clone() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.ethernet_address + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.waker.register(cx.waker()); + s.state + }) + } +} + +pub struct RxToken<'a, const MTU: usize> { + rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.try_recv()); + let r = f(&mut pkt.buf[..pkt.len]); + self.rx.recv_done(); + r + } +} + +pub struct TxToken<'a, const MTU: usize> { + tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { + fn consume(mut self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.try_send()); + let r = f(&mut pkt.buf[..len]); + pkt.len = len; + self.tx.send_done(); + r + } +} + +mod zerocopy_channel { + use core::cell::RefCell; + use core::future::poll_fn; + use core::marker::PhantomData; + use core::task::{Context, Poll}; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use embassy_sync::blocking_mutex::Mutex; + use embassy_sync::waitqueue::WakerRegistration; + + pub struct Channel<'a, M: RawMutex, T> { + buf: *mut T, + phantom: PhantomData<&'a mut T>, + state: Mutex>, + } + + impl<'a, M: RawMutex, T> Channel<'a, M, T> { + pub fn new(buf: &'a mut [T]) -> Self { + let len = buf.len(); + assert!(len != 0); + + Self { + buf: buf.as_mut_ptr(), + phantom: PhantomData, + state: Mutex::new(RefCell::new(State { + len, + front: 0, + back: 0, + full: false, + send_waker: WakerRegistration::new(), + recv_waker: WakerRegistration::new(), + })), + } + } + + pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { + (Sender { channel: self }, Receiver { channel: self }) + } + } + + pub struct Sender<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Sender<'a, M, T> { + pub fn borrow(&mut self) -> Sender<'_, M, T> { + Sender { channel: self.channel } + } + + pub fn try_send(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn send(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(i), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn send_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().push_done()) + } + } + pub struct Receiver<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Receiver<'a, M, T> { + pub fn borrow(&mut self) -> Receiver<'_, M, T> { + Receiver { channel: self.channel } + } + + pub fn try_recv(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn recv(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(i), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn recv_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().pop_done()) + } + } + + struct State { + len: usize, + + /// Front index. Always 0..=(N-1) + front: usize, + /// Back index. Always 0..=(N-1). + back: usize, + + /// Used to distinguish "empty" and "full" cases when `front == back`. + /// May only be `true` if `front == back`, always `false` otherwise. + full: bool, + + send_waker: WakerRegistration, + recv_waker: WakerRegistration, + } + + impl State { + fn increment(&self, i: usize) -> usize { + if i + 1 == self.len { + 0 + } else { + i + 1 + } + } + + fn is_full(&self) -> bool { + self.full + } + + fn is_empty(&self) -> bool { + self.front == self.back && !self.full + } + + fn push_index(&mut self) -> Option { + match self.is_full() { + true => None, + false => Some(self.back), + } + } + + fn push_done(&mut self) { + assert!(!self.is_full()); + self.back = self.increment(self.back); + if self.back == self.front { + self.full = true; + } + self.send_waker.wake(); + } + + fn pop_index(&mut self) -> Option { + match self.is_empty() { + true => None, + false => Some(self.front), + } + } + + fn pop_done(&mut self) { + assert!(!self.is_empty()); + self.front = self.increment(self.front); + self.full = false; + self.recv_waker.wake(); + } + } +} diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index d0742ceb..1e567bb9 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,7 +19,7 @@ default = ["usbd-hid"] 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" } -embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 093afeff..7ecf693d 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,81 +1,45 @@ -use core::cell::RefCell; -use core::mem::MaybeUninit; -use core::task::Context; - use embassy_futures::select::{select, Either}; -use embassy_net_driver::{Capabilities, LinkState, Medium}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::blocking_mutex::Mutex; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; use embassy_usb_driver::Driver; use super::{CdcNcmClass, Receiver, Sender}; -pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { - rx: [PacketBuf; N_RX], - tx: [PacketBuf; N_TX], - inner: MaybeUninit>, +pub struct State { + ch_state: ch::State, } -impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { - const NEW_PACKET: PacketBuf = PacketBuf::new(); - +impl State { pub const fn new() -> Self { Self { - rx: [Self::NEW_PACKET; N_RX], - tx: [Self::NEW_PACKET; N_TX], - inner: MaybeUninit::uninit(), + ch_state: ch::State::new(), } } } -struct StateInner<'d, const MTU: usize> { - rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - link_state: Mutex>, -} - -/// State of the LinkState -struct LinkStateState { - state: LinkState, - waker: WakerRegistration, -} - pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { tx_usb: Sender<'d, D>, - tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, rx_usb: Receiver<'d, D>, - rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + ch: ch::Runner<'d, MTU>, } impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { pub async fn run(mut self) -> ! { + let (mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { loop { trace!("WAITING for connection"); - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.state = LinkState::Down; - s.waker.wake(); - }); + rx_chan.set_link_state(LinkState::Down); self.rx_usb.wait_connection().await.unwrap(); trace!("Connected"); - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.state = LinkState::Up; - s.waker.wake(); - }); + rx_chan.set_link_state(LinkState::Up); loop { - let p = self.rx_chan.send().await; - match self.rx_usb.read_packet(&mut p.buf).await { - Ok(n) => { - p.len = n; - self.rx_chan.send_done(); - } + let p = rx_chan.rx_buf().await; + match self.rx_usb.read_packet(p).await { + Ok(n) => rx_chan.rx_done(n), Err(e) => { warn!("error reading packet: {:?}", e); break; @@ -86,11 +50,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { }; let tx_fut = async move { loop { - let p = self.tx_chan.recv().await; - if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { + let p = tx_chan.tx_buf().await; + if let Err(e) = self.tx_usb.write_packet(p).await { warn!("Failed to TX packet: {:?}", e); } - self.tx_chan.recv_done(); + tx_chan.tx_done(); } }; match select(rx_fut, tx_fut).await { @@ -100,350 +64,26 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { } } +// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? +//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; +pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; + impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { pub fn into_embassy_net_device( self, - state: &'d mut State<'d, MTU, N_RX, N_TX>, + state: &'d mut State, ethernet_address: [u8; 6], ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - - let mut caps = Capabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = Medium::Ethernet; - - let state = state.inner.write(StateInner { - rx: zerocopy_channel::Channel::new(&mut state.rx[..]), - tx: zerocopy_channel::Channel::new(&mut state.tx[..]), - link_state: Mutex::new(RefCell::new(LinkStateState { - state: LinkState::Down, - waker: WakerRegistration::new(), - })), - }); - - let (rx_sender, rx_receiver) = state.rx.split(); - let (tx_sender, tx_receiver) = state.tx.split(); + let (runner, device) = ch::new(&mut state.ch_state, ethernet_address); ( Runner { tx_usb, - tx_chan: tx_receiver, rx_usb, - rx_chan: rx_sender, - link_state: &state.link_state, - }, - Device { - caps, - ethernet_address, - link_state: &state.link_state, - rx: rx_receiver, - tx: tx_sender, + ch: runner, }, + device, ) } } - -pub struct PacketBuf { - len: usize, - buf: [u8; MTU], -} - -impl PacketBuf { - pub const fn new() -> Self { - Self { len: 0, buf: [0; MTU] } - } -} - -pub struct Device<'d, const MTU: usize> { - rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, - tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, - caps: Capabilities, - ethernet_address: [u8; 6], -} - -impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { - type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; - type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { - Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) - } else { - None - } - } - - /// Construct a transmit token. - fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.tx.poll_send(cx).is_ready() { - Some(TxToken { tx: self.tx.borrow() }) - } else { - None - } - } - - /// Get a description of device capabilities. - fn capabilities(&self) -> Capabilities { - self.caps.clone() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.ethernet_address - } - - fn link_state(&mut self, cx: &mut Context) -> LinkState { - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.waker.register(cx.waker()); - s.state - }) - } -} - -pub struct RxToken<'a, const MTU: usize> { - rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, -} - -impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { - fn consume(mut self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.try_recv()); - let r = f(&mut pkt.buf[..pkt.len]); - self.rx.recv_done(); - r - } -} - -pub struct TxToken<'a, const MTU: usize> { - tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, -} - -impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { - fn consume(mut self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.tx.try_send()); - let r = f(&mut pkt.buf[..len]); - pkt.len = len; - self.tx.send_done(); - r - } -} - -mod zerocopy_channel { - use core::cell::RefCell; - use core::future::poll_fn; - use core::marker::PhantomData; - use core::task::{Context, Poll}; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use embassy_sync::blocking_mutex::Mutex; - use embassy_sync::waitqueue::WakerRegistration; - - pub struct Channel<'a, M: RawMutex, T> { - buf: *mut T, - phantom: PhantomData<&'a mut T>, - state: Mutex>, - } - - impl<'a, M: RawMutex, T> Channel<'a, M, T> { - pub fn new(buf: &'a mut [T]) -> Self { - let len = buf.len(); - assert!(len != 0); - - Self { - buf: buf.as_mut_ptr(), - phantom: PhantomData, - state: Mutex::new(RefCell::new(State { - len, - front: 0, - back: 0, - full: false, - send_waker: WakerRegistration::new(), - recv_waker: WakerRegistration::new(), - })), - } - } - - pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { - (Sender { channel: self }, Receiver { channel: self }) - } - } - - pub struct Sender<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Sender<'a, M, T> { - pub fn borrow(&mut self) -> Sender<'_, M, T> { - Sender { channel: self.channel } - } - - pub fn try_send(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn send(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(i), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn send_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().push_done()) - } - } - pub struct Receiver<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Receiver<'a, M, T> { - pub fn borrow(&mut self) -> Receiver<'_, M, T> { - Receiver { channel: self.channel } - } - - pub fn try_recv(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn recv(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(i), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn recv_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().pop_done()) - } - } - - struct State { - len: usize, - - /// Front index. Always 0..=(N-1) - front: usize, - /// Back index. Always 0..=(N-1). - back: usize, - - /// Used to distinguish "empty" and "full" cases when `front == back`. - /// May only be `true` if `front == back`, always `false` otherwise. - full: bool, - - send_waker: WakerRegistration, - recv_waker: WakerRegistration, - } - - impl State { - fn increment(&self, i: usize) -> usize { - if i + 1 == self.len { - 0 - } else { - i + 1 - } - } - - fn is_full(&self) -> bool { - self.full - } - - fn is_empty(&self) -> bool { - self.front == self.back && !self.full - } - - fn push_index(&mut self) -> Option { - match self.is_full() { - true => None, - false => Some(self.back), - } - } - - fn push_done(&mut self) { - assert!(!self.is_full()); - self.back = self.increment(self.back); - if self.back == self.front { - self.full = true; - } - self.send_waker.wake(); - } - - fn pop_index(&mut self) -> Option { - match self.is_empty() { - true => None, - false => Some(self.front), - } - } - - fn pop_done(&mut self) { - assert!(!self.is_empty()); - self.front = self.increment(self.front); - self.full = false; - self.recv_waker.wake(); - } - } -} From 97f9f248f4ca09ab8b2bbb3806d9c23ccc038224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 26 Dec 2022 09:35:23 +0100 Subject: [PATCH 248/374] usb cdc-acm: Remove unused call management descriptor --- embassy-usb/src/class/cdc_acm.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 09bb1cc8..7f66c5d5 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -18,7 +18,6 @@ const CDC_PROTOCOL_NONE: u8 = 0x00; const CS_INTERFACE: u8 = 0x24; const CDC_TYPE_HEADER: u8 = 0x00; -const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; const CDC_TYPE_ACM: u8 = 0x02; const CDC_TYPE_UNION: u8 = 0x06; @@ -197,14 +196,6 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { data_if.into(), // bSubordinateInterface ], ); - alt.descriptor( - CS_INTERFACE, - &[ - CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype - 0x00, // bmCapabilities - data_if.into(), // bDataInterface - ], - ); let comm_ep = alt.endpoint_interrupt_in(8, 255); From 4e0d563997017492aa46025e8809653c14e0f875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 26 Dec 2022 09:36:04 +0100 Subject: [PATCH 249/374] usb cdc-acm: Set flag for supported capabilities --- embassy-usb/src/class/cdc_acm.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 7f66c5d5..84db2062 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -185,7 +185,10 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { CS_INTERFACE, &[ CDC_TYPE_ACM, // bDescriptorSubtype - 0x00, // bmCapabilities + 0x02, // bmCapabilities: + // D1: Device supports the request combination of + // Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, + // and the Notification Serial_State. ], ); alt.descriptor( From 41d63169849e380a353a524c4b3591a3551bc4a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 22:30:22 +0100 Subject: [PATCH 250/374] rp: switch to released 0.2.1 pio crate. --- embassy-rp/Cargo.toml | 5 +---- examples/rp/Cargo.toml | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 6d5bc91e..209c665b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -71,7 +71,4 @@ embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} paste = "1.0" pio-proc = {version= "0.2", optional = true} -pio = {version= "0.2", optional = true} - -[patch.crates-io] -pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file +pio = {version= "0.2.1", optional = true} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index afd1042a..f07684f2 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -36,10 +36,7 @@ embedded-storage = { version = "0.3" } static_cell = "1.0.0" log = "0.4" pio-proc = "0.2" -pio = "0.2" +pio = "0.2.1" [profile.release] debug = true - -[patch.crates-io] -pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file From 4a4b59369482572c415487b9d90166e7c19b592a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Dec 2022 01:04:55 +0100 Subject: [PATCH 251/374] net; allow changing MAC addr at runtime. --- embassy-net-driver-channel/src/lib.rs | 56 +++++++++++++++++---------- embassy-net/src/lib.rs | 16 ++++---- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 369dc5a9..073edb41 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -34,24 +34,25 @@ impl State { rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - link_state: Mutex>, + shared: Mutex>, } /// State of the LinkState -struct LinkStateState { - state: LinkState, +struct Shared { + link_state: LinkState, waker: WakerRegistration, + ethernet_address: [u8; 6], } pub struct Runner<'d, const MTU: usize> { tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + shared: &'d Mutex>, } pub struct RxRunner<'d, const MTU: usize> { rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + shared: &'d Mutex>, } pub struct TxRunner<'d, const MTU: usize> { @@ -62,7 +63,7 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { ( RxRunner { - link_state: self.link_state, + shared: self.shared, rx_chan: self.rx_chan, }, TxRunner { tx_chan: self.tx_chan }, @@ -70,9 +71,17 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { } pub fn set_link_state(&mut self, state: LinkState) { - self.link_state.lock(|s| { + self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.state = state; + s.link_state = state; + s.waker.wake(); + }); + } + + pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.ethernet_address = address; s.waker.wake(); }); } @@ -124,9 +133,17 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { impl<'d, const MTU: usize> RxRunner<'d, MTU> { pub fn set_link_state(&mut self, state: LinkState) { - self.link_state.lock(|s| { + self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.state = state; + s.link_state = state; + s.waker.wake(); + }); + } + + pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.ethernet_address = address; s.waker.wake(); }); } @@ -194,8 +211,9 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( let state = unsafe { &mut *state_uninit }.write(StateInner { rx: zerocopy_channel::Channel::new(&mut state.rx[..]), tx: zerocopy_channel::Channel::new(&mut state.tx[..]), - link_state: Mutex::new(RefCell::new(LinkStateState { - state: LinkState::Down, + shared: Mutex::new(RefCell::new(Shared { + link_state: LinkState::Down, + ethernet_address, waker: WakerRegistration::new(), })), }); @@ -207,12 +225,11 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( Runner { tx_chan: tx_receiver, rx_chan: rx_sender, - link_state: &state.link_state, + shared: &state.shared, }, Device { caps, - ethernet_address, - link_state: &state.link_state, + shared: &state.shared, rx: rx_receiver, tx: tx_sender, }, @@ -233,9 +250,8 @@ impl PacketBuf { pub struct Device<'d, const MTU: usize> { rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + shared: &'d Mutex>, caps: Capabilities, - ethernet_address: [u8; 6], } impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { @@ -265,14 +281,14 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { } fn ethernet_address(&self) -> [u8; 6] { - self.ethernet_address + self.shared.lock(|s| s.borrow().ethernet_address) } fn link_state(&mut self, cx: &mut Context) -> LinkState { - self.link_state.lock(|s| { + self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.waker.register(cx.waker()); - s.state + s.link_state }) } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b58c9cf3..e4a4218e 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -111,20 +111,13 @@ impl Stack { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; - #[cfg(feature = "medium-ethernet")] - let ethernet_addr = if medium == Medium::Ethernet { - device.ethernet_address() - } else { - [0, 0, 0, 0, 0, 0] - }; - let mut b = InterfaceBuilder::new(); b = b.ip_addrs(&mut resources.addresses[..]); b = b.random_seed(random_seed); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { - b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); + b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); b = b.routes(Routes::new(&mut resources.routes[..])); } @@ -261,6 +254,13 @@ impl Inner { fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); + #[cfg(feature = "medium-ethernet")] + if self.device.capabilities().medium == Medium::Ethernet { + s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress( + self.device.ethernet_address(), + ))); + } + let timestamp = instant_to_smoltcp(Instant::now()); let mut smoldev = DriverAdapter { cx: Some(cx), From 771806be790a2758f1314d6460defe7c2f0d3e99 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Dec 2022 01:07:58 +0100 Subject: [PATCH 252/374] net/chan: split state runner. --- embassy-net-driver-channel/src/lib.rs | 26 +++++++++++++------- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 6 ++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 073edb41..0c8dcc22 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -50,9 +50,13 @@ pub struct Runner<'d, const MTU: usize> { shared: &'d Mutex>, } +#[derive(Clone, Copy)] +pub struct StateRunner<'d> { + shared: &'d Mutex>, +} + pub struct RxRunner<'d, const MTU: usize> { rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - shared: &'d Mutex>, } pub struct TxRunner<'d, const MTU: usize> { @@ -60,16 +64,18 @@ pub struct TxRunner<'d, const MTU: usize> { } impl<'d, const MTU: usize> Runner<'d, MTU> { - pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { + pub fn split(self) -> (StateRunner<'d>, RxRunner<'d, MTU>, TxRunner<'d, MTU>) { ( - RxRunner { - shared: self.shared, - rx_chan: self.rx_chan, - }, + StateRunner { shared: self.shared }, + RxRunner { rx_chan: self.rx_chan }, TxRunner { tx_chan: self.tx_chan }, ) } + pub fn state_runner(&self) -> StateRunner<'d> { + StateRunner { shared: self.shared } + } + pub fn set_link_state(&mut self, state: LinkState) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); @@ -131,8 +137,8 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { } } -impl<'d, const MTU: usize> RxRunner<'d, MTU> { - pub fn set_link_state(&mut self, state: LinkState) { +impl<'d> StateRunner<'d> { + pub fn set_link_state(&self, state: LinkState) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.link_state = state; @@ -140,14 +146,16 @@ impl<'d, const MTU: usize> RxRunner<'d, MTU> { }); } - pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + pub fn set_ethernet_address(&self, address: [u8; 6]) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.ethernet_address = address; s.waker.wake(); }); } +} +impl<'d, const MTU: usize> RxRunner<'d, MTU> { pub async fn rx_buf(&mut self) -> &mut [u8] { let p = self.rx_chan.send().await; &mut p.buf diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 7ecf693d..501df2d8 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -25,16 +25,16 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { pub async fn run(mut self) -> ! { - let (mut rx_chan, mut tx_chan) = self.ch.split(); + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { loop { trace!("WAITING for connection"); - rx_chan.set_link_state(LinkState::Down); + state_chan.set_link_state(LinkState::Down); self.rx_usb.wait_connection().await.unwrap(); trace!("Connected"); - rx_chan.set_link_state(LinkState::Up); + state_chan.set_link_state(LinkState::Up); loop { let p = rx_chan.rx_buf().await; From e4f457646f216f36cd86fcfe54af0d7956d17932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 27 Dec 2022 10:20:51 +0100 Subject: [PATCH 253/374] rp: Fill and empty FIFOs in buffered uart interrupt Fixes an issue where only the first byte was transmitted. Should improve throughput aswell. --- embassy-rp/src/uart/buffered.rs | 40 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index e89970d1..1d1fe37c 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -402,27 +402,45 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { } let mut rx_writer = s.rx_buf.writer(); - if !r.uartfr().read().rxfe() { - let val = r.uartdr().read().data(); - if !rx_writer.push_one(val) { - warn!("RX buffer full, discard received byte"); + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + for rx_byte in rx_buf { + if r.uartfr().read().rxfe() { + break; } + *rx_byte = r.uartdr().read().data(); + n_read += 1; + } + if n_read > 0 { + rx_writer.push_done(n_read); s.rx_waker.wake(); } // TX let mut tx_reader = s.tx_buf.reader(); - if let Some(val) = tx_reader.pop_one() { - r.uartimsc().modify(|w| { - w.set_txim(true); - }); - r.uartdr().write(|w| w.set_data(val)); - s.tx_waker.wake(); - } else { + let tx_buf = tx_reader.pop_slice(); + if tx_buf.len() == 0 { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { w.set_txim(false); }); + } else { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; + } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; + } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } } } } From 2c8080b0aed4d998dfe2b158c63121b515213a54 Mon Sep 17 00:00:00 2001 From: Matthias Devlamynck Date: Thu, 29 Dec 2022 17:26:49 +0100 Subject: [PATCH 254/374] Add Clone & Copy on embassy_nrf::gpio::Level --- embassy-nrf/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index bb64e41e..b7230302 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -70,7 +70,7 @@ impl<'d, T: Pin> Input<'d, T> { } /// Digital input or output level. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Level { /// Logical low. From 72bb4f8798a4f42a8d0e409261e06f9a2d2f03ca Mon Sep 17 00:00:00 2001 From: Kentaro Okuda Date: Fri, 30 Dec 2022 08:53:16 -0500 Subject: [PATCH 255/374] Fix a typo --- embassy-nrf/src/uarte.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 4703c1a5..031cf82f 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -166,7 +166,7 @@ impl<'d, T: Instance> Uarte<'d, T> { } /// Split the Uarte into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to + /// particularly useful when having two tasks correlating to /// transmitting and receiving. pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { (self.tx, self.rx) From 9428c40c8de1285271a5e6ba9ad2a7fed8a9475e Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 3 Jan 2023 22:58:56 +0100 Subject: [PATCH 256/374] embassy-boot (rp): Add WatchdogFlash --- embassy-boot/rp/Cargo.toml | 2 + embassy-boot/rp/src/lib.rs | 51 ++++++++++++++++++++++- examples/boot/application/rp/src/bin/a.rs | 7 ++++ examples/boot/bootloader/rp/Cargo.toml | 2 + examples/boot/bootloader/rp/src/main.rs | 6 +-- 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml index 93099b23..ffc36a4e 100644 --- a/embassy-boot/rp/Cargo.toml +++ b/embassy-boot/rp/Cargo.toml @@ -20,6 +20,8 @@ log = { version = "0.4", optional = true } embassy-sync = { path = "../../embassy-sync" } embassy-rp = { path = "../../embassy-rp", default-features = false, features = ["nightly"] } embassy-boot = { path = "../boot", default-features = false } +embassy-time = { path = "../../embassy-time", features = ["nightly"] } + cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 85fc8182..6eb429aa 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -5,7 +5,11 @@ mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; -use embassy_rp::flash::{ERASE_SIZE, WRITE_SIZE}; +use embassy_rp::flash::{Flash, ERASE_SIZE, WRITE_SIZE}; +use embassy_rp::peripherals::{FLASH, WATCHDOG}; +use embassy_rp::watchdog::Watchdog; +use embassy_time::Duration; +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. pub struct BootLoader { @@ -88,3 +92,48 @@ impl Default for BootLoader { Self::new(active, dfu, state) } } + +/// A flash implementation that wraps FLASH and will pet a watchdog when touching flash. +pub struct WatchdogFlash<'d, const SIZE: usize> { + flash: Flash<'d, FLASH, SIZE>, + watchdog: Watchdog, +} + +impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { + /// Start a new watchdog with a given flash and watchdog peripheral and a timeout + pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { + let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash); + let mut watchdog = Watchdog::new(watchdog); + watchdog.start(timeout); + Self { flash, watchdog } + } +} + +impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { + type Error = as ErrorType>::Error; +} + +impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { + const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; + const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.watchdog.feed(); + self.flash.erase(from, to) + } + fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { + self.watchdog.feed(); + self.flash.write(offset, data) + } +} + +impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { + const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; + fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { + self.watchdog.feed(); + self.flash.read(offset, data) + } + fn capacity(&self) -> usize { + self.flash.capacity() + } +} diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 3736c914..e3ac634c 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -7,6 +7,7 @@ use embassy_boot_rp::*; use embassy_executor::Spawner; use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; +use embassy_rp::watchdog::Watchdog; use embassy_time::{Duration, Timer}; #[cfg(feature = "panic-probe")] use panic_probe as _; @@ -21,11 +22,16 @@ async fn main(_s: Spawner) { let p = embassy_rp::init(Default::default()); let mut led = Output::new(p.PIN_25, Level::Low); + // Override bootloader watchdog + let mut watchdog = Watchdog::new(p.WATCHDOG); + watchdog.start(Duration::from_secs(8)); + let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); let mut updater = FirmwareUpdater::default(); Timer::after(Duration::from_secs(5)).await; + watchdog.feed(); led.set_high(); let mut offset = 0; let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); @@ -43,6 +49,7 @@ async fn main(_s: Spawner) { .unwrap(); offset += chunk.len(); } + watchdog.feed(); defmt::info!("firmware written, marking update"); updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); Timer::after(Duration::from_secs(2)).await; diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 580ced22..c0b576cf 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -11,6 +11,8 @@ defmt-rtt = { version = "0.4", optional = true } embassy-rp = { path = "../../../../embassy-rp", default-features = false, features = ["nightly"] } embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } +embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } + cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 5028ec68..4defa01f 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -5,8 +5,8 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_rp::*; -use embassy_rp::flash::{Flash, ERASE_SIZE}; -use embassy_rp::peripherals::FLASH; +use embassy_rp::flash::ERASE_SIZE; +use embassy_time::Duration; const FLASH_SIZE: usize = 2 * 1024 * 1024; @@ -23,7 +23,7 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let flash: Flash<'_, FLASH, FLASH_SIZE> = Flash::new(p.FLASH); + let flash: WatchdogFlash<'_, FLASH_SIZE> = WatchdogFlash::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); From 413f33948927b4b82a7a9cdd835d226a69fdcefc Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 3 Jan 2023 23:34:50 +0100 Subject: [PATCH 257/374] Cleanup --- embassy-boot/rp/src/lib.rs | 2 +- examples/boot/bootloader/rp/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 6eb429aa..6df34133 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -93,7 +93,7 @@ impl Default for BootLoader { } } -/// A flash implementation that wraps FLASH and will pet a watchdog when touching flash. +/// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { flash: Flash<'d, FLASH, SIZE>, watchdog: Watchdog, diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 4defa01f..fb7f0522 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -23,7 +23,7 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let flash: WatchdogFlash<'_, FLASH_SIZE> = WatchdogFlash::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); + let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); From 651eec02423c42afb80e8f2eaedb4b618148a26e Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 4 Jan 2023 10:19:39 +1100 Subject: [PATCH 258/374] Pass WDT config around By passing WDT config around we can control it more easily and promote sharing it between files. --- embassy-boot/nrf/src/lib.rs | 6 +----- examples/boot/bootloader/nrf/src/main.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 205bbd6d..f40ae62d 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -149,11 +149,7 @@ pub struct WatchdogFlash<'d> { impl<'d> WatchdogFlash<'d> { /// Start a new watchdog with a given flash and WDT peripheral and a timeout - pub fn start(flash: Nvmc<'d>, wdt: WDT, timeout: u32) -> Self { - let mut config = wdt::Config::default(); - config.timeout_ticks = 32768 * timeout; // timeout seconds - config.run_during_sleep = true; - config.run_during_debug_halt = false; + pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self { let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) { Ok(x) => x, Err(_) => { diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 8266206b..aca3b857 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -6,6 +6,7 @@ use cortex_m_rt::{entry, exception}; use defmt_rtt as _; use embassy_boot_nrf::*; use embassy_nrf::nvmc::Nvmc; +use embassy_nrf::wdt; #[entry] fn main() -> ! { @@ -20,8 +21,14 @@ fn main() -> ! { */ let mut bl = BootLoader::default(); + + let mut wdt_config = wdt::Config::default(); + wdt_config.timeout_ticks = 32768 * 5; // timeout seconds + wdt_config.run_during_sleep = true; + wdt_config.run_during_debug_halt = false; + let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), + WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config), ))); unsafe { bl.load(start) } } From 6e6c3cbebcf0cfdb07622df803584f3fbc6a491a Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 4 Jan 2023 10:27:16 +1100 Subject: [PATCH 259/374] Cleaned up some doc and memory layout The memory layout of the s140 crept into a number of memory files, which can cause confusion. --- examples/boot/application/nrf/README.md | 4 ++++ examples/boot/application/nrf/memory-bl.x | 2 +- examples/boot/application/nrf/memory.x | 2 +- examples/boot/bootloader/nrf/memory-bm.x | 2 +- examples/boot/bootloader/nrf/memory.x | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index 703377a2..a6719b50 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md @@ -32,3 +32,7 @@ cargo objcopy --release --bin b -- -O binary b.bin ``` cargo flash --release --bin a --chip nRF52840_xxAA ``` + +You should then see a solid LED. Pressing button 1 will cause the DFU to be loaded by the bootloader. Upon +successfully loading, you'll see the LED flash. After 5 seconds, because there is no petting of the watchdog, +you'll see the LED go solid again. This indicates that the bootloader has reverted the update. \ No newline at end of file diff --git a/examples/boot/application/nrf/memory-bl.x b/examples/boot/application/nrf/memory-bl.x index 8a32b905..257d6564 100644 --- a/examples/boot/application/nrf/memory-bl.x +++ b/examples/boot/application/nrf/memory-bl.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); diff --git a/examples/boot/application/nrf/memory.x b/examples/boot/application/nrf/memory.x index 3a54ca46..c6926e42 100644 --- a/examples/boot/application/nrf/memory.x +++ b/examples/boot/application/nrf/memory.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K FLASH : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); diff --git a/examples/boot/bootloader/nrf/memory-bm.x b/examples/boot/bootloader/nrf/memory-bm.x index 8a32b905..257d6564 100644 --- a/examples/boot/bootloader/nrf/memory-bm.x +++ b/examples/boot/bootloader/nrf/memory-bm.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); diff --git a/examples/boot/bootloader/nrf/memory.x b/examples/boot/bootloader/nrf/memory.x index 8a32b905..257d6564 100644 --- a/examples/boot/bootloader/nrf/memory.x +++ b/examples/boot/bootloader/nrf/memory.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); From 8497f98de244f0f8800df78d6e83a2fb886016bf Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 4 Jan 2023 11:07:07 +1100 Subject: [PATCH 260/374] Provides a means of obtaining the current WDT config Obtaining the current WDT config is important so that we do not have to duplication configuration around the place. A constructor method has been introduced that returns WDT config in accordance with how the register is presently configured. The bootloader example has also been updated to show the watchdog can be obtained and used. --- embassy-nrf/src/wdt.rs | 24 ++++++++++++++++++++++ examples/boot/application/nrf/src/bin/a.rs | 18 ++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index 8760aa30..330ca98b 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -23,6 +23,30 @@ pub struct Config { pub run_during_debug_halt: bool, } +impl Config { + /// Create a config structure from the current configuration of the WDT + /// peripheral. + pub fn try_new(_wdt: &peripherals::WDT) -> Option { + let r = unsafe { &*WDT::ptr() }; + + #[cfg(not(feature = "_nrf9160"))] + let runstatus = r.runstatus.read().runstatus().bit(); + #[cfg(feature = "_nrf9160")] + let runstatus = r.runstatus.read().runstatuswdt().bit(); + + if runstatus { + let config = r.config.read(); + Some(Self { + timeout_ticks: r.crv.read().bits(), + run_during_sleep: config.sleep().bit(), + run_during_debug_halt: config.halt().bit(), + }) + } else { + None + } + } +} + impl Default for Config { fn default() -> Self { Self { diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 7a404a91..83191f38 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -8,6 +8,7 @@ use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::nvmc::Nvmc; +use embassy_nrf::wdt::{self, Watchdog}; use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); @@ -20,6 +21,23 @@ async fn main(_spawner: Spawner) { //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); //let mut button = Input::new(p.P1_02, Pull::Up); + // The following code block illustrates how to obtain a watchdog that is configured + // as per the existing watchdog. Ordinarily, we'd use the handle returned to "pet" the + // watchdog periodically. If we don't, and we're not going to for this example, then + // the watchdog will cause the device to reset as per its configured timeout in the bootloader. + // This helps is avoid a situation where new firmware might be bad and block our executor. + // If firmware is bad in this way then the bootloader will revert to any previous version. + let wdt_config = wdt::Config::try_new(&p.WDT).unwrap(); + let (_wdt, [_wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) { + Ok(x) => x, + Err(_) => { + // Watchdog already active with the wrong number of handles, waiting for it to timeout... + loop { + cortex_m::asm::wfe(); + } + } + }; + let nvmc = Nvmc::new(p.NVMC); let mut nvmc = BlockingAsync::new(nvmc); From 5aa59e97372e2efed4e35625b754364b1b4839a3 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 4 Jan 2023 12:57:19 +0100 Subject: [PATCH 261/374] feat(stm32): Let uart implement embedded-io Read/Write --- embassy-stm32/src/usart/mod.rs | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6f8b6a9e..233b56ba 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -910,6 +910,46 @@ mod eh1 { } } +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eio { + use embedded_io::asynch::{Read, Write}; + use embedded_io::Io; + + use super::*; + + impl Io for Uart<'_, T, TxDma, RxDma> + where + T: BasicInstance, + { + type Error = Error; + } + + impl Read for Uart<'_, T, TxDma, RxDma> + where + T: BasicInstance, + RxDma: super::RxDma, + { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_until_idle(buf).await + } + } + + impl Write for Uart<'_, T, TxDma, RxDma> + where + T: BasicInstance, + TxDma: super::TxDma, + { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } +} + #[cfg(all( feature = "unstable-traits", feature = "nightly", From 2332d8cd2396a01fdeaf61dce814da79503c8f70 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 4 Jan 2023 13:35:01 +0100 Subject: [PATCH 262/374] feat: compile bootloader examples for nRF91 * Add nRF91 as target in CI builds * Add example linker scripts for nrf91 * Make less nRF52 assumptions example config * Add llvm-tools-preview required for cargo objcopy example --- ci.sh | 5 ++++- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/nrf/README.md | 10 +++++----- .../boot/application/nrf/memory-bl-nrf91.x | 19 +++++++++++++++++++ examples/boot/application/nrf/memory-nrf91.x | 16 ++++++++++++++++ examples/boot/application/nrf/src/bin/a.rs | 6 ++++++ examples/boot/application/nrf/src/bin/b.rs | 5 ++++- rust-toolchain.toml | 2 +- 8 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 examples/boot/application/nrf/memory-bl-nrf91.x create mode 100644 examples/boot/application/nrf/memory-nrf91.x diff --git a/ci.sh b/ci.sh index 14a48454..f59f3f46 100755 --- a/ci.sh +++ b/ci.sh @@ -81,6 +81,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ @@ -106,7 +107,8 @@ cargo batch \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/nrf --bin b \ + --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 --out-dir out/examples/boot/nrf --bin b \ + --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns --out-dir out/examples/boot/nrf --bin b \ --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/rp --bin b \ --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f3 --bin b \ --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f7 --bin b \ @@ -116,6 +118,7 @@ cargo batch \ --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32l4 --bin b \ --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wl --bin b \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 1e7a5a84..9679bbc5 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } -embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly", "nrf52840"] } +embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index a6719b50..5d45f629 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md @@ -1,6 +1,6 @@ # Examples using bootloader -Example for nRF52 demonstrating the bootloader. The example consists of application binaries, 'a' +Example for nRF demonstrating the bootloader. The example consists of application binaries, 'a' which allows you to press a button to start the DFU process, and 'b' which is the updated application. @@ -20,19 +20,19 @@ application. cp memory-bl.x ../../bootloader/nrf/memory.x # Flash bootloader -cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --release --chip nRF52840_xxAA +cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA # Build 'b' cargo build --release --bin b # Generate binary for 'b' -cargo objcopy --release --bin b -- -O binary b.bin +cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin ``` # Flash `a` (which includes b.bin) ``` -cargo flash --release --bin a --chip nRF52840_xxAA +cargo flash --release --bin a --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --chip nRF52840_xxAA ``` You should then see a solid LED. Pressing button 1 will cause the DFU to be loaded by the bootloader. Upon successfully loading, you'll see the LED flash. After 5 seconds, because there is no petting of the watchdog, -you'll see the LED go solid again. This indicates that the bootloader has reverted the update. \ No newline at end of file +you'll see the LED go solid again. This indicates that the bootloader has reverted the update. diff --git a/examples/boot/application/nrf/memory-bl-nrf91.x b/examples/boot/application/nrf/memory-bl-nrf91.x new file mode 100644 index 00000000..14ceffa7 --- /dev/null +++ b/examples/boot/application/nrf/memory-bl-nrf91.x @@ -0,0 +1,19 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* Assumes Secure Partition Manager (SPM) flashed at the start */ + FLASH : ORIGIN = 0x00050000, LENGTH = 24K + BOOTLOADER_STATE : ORIGIN = 0x00056000, LENGTH = 4K + ACTIVE : ORIGIN = 0x00057000, LENGTH = 64K + DFU : ORIGIN = 0x00067000, LENGTH = 68K + RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 32K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); + +__bootloader_active_start = ORIGIN(ACTIVE); +__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE); + +__bootloader_dfu_start = ORIGIN(DFU); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); diff --git a/examples/boot/application/nrf/memory-nrf91.x b/examples/boot/application/nrf/memory-nrf91.x new file mode 100644 index 00000000..2bc13c0d --- /dev/null +++ b/examples/boot/application/nrf/memory-nrf91.x @@ -0,0 +1,16 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* Assumes Secure Partition Manager (SPM) flashed at the start */ + BOOTLOADER : ORIGIN = 0x00050000, LENGTH = 24K + BOOTLOADER_STATE : ORIGIN = 0x00056000, LENGTH = 4K + FLASH : ORIGIN = 0x00057000, LENGTH = 64K + DFU : ORIGIN = 0x00067000, LENGTH = 68K + RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 32K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); + +__bootloader_dfu_start = ORIGIN(DFU); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 83191f38..090a05b2 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -16,11 +16,17 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); + let mut button = Input::new(p.P0_11, Pull::Up); let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); //let mut button = Input::new(p.P1_02, Pull::Up); + // nRF91 DK + // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); + // let mut button = Input::new(p.P0_06, Pull::Up); + // The following code block illustrates how to obtain a watchdog that is configured // as per the existing watchdog. Ordinarily, we'd use the handle returned to "pet" the // watchdog periodically. If we don't, and we're not going to for this example, then diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 1373f277..15ebce5f 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -12,7 +12,10 @@ use panic_reset as _; async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); - //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); + // let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); + + // nRF91 DK + // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); loop { led.set_high(); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 55539405..f2adc435 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,7 +2,7 @@ # https://rust-lang.github.io/rustup-components-history [toolchain] channel = "nightly-2022-11-22" -components = [ "rust-src", "rustfmt" ] +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", "thumbv7m-none-eabi", From 68c186309f5da13266118a5c6b90c9082f73cbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 1 Jan 2023 21:34:20 +0100 Subject: [PATCH 263/374] rp: Common init function for BufferedUart BufferedUart, BufferedUartRx and BufferedUartTX can all use the same init code. --- embassy-rp/src/uart/buffered.rs | 186 +++++++++++--------------------- 1 file changed, 64 insertions(+), 122 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 1d1fe37c..d853618a 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -38,6 +38,38 @@ pub struct BufferedUartTx<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } +fn init<'d, T: Instance + 'd>( + irq: PeripheralRef<'d, T::Interrupt>, + tx: Option>, + rx: Option>, + rts: Option>, + cts: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, +) { + let regs = T::regs(); + unsafe { + regs.uartimsc().modify(|w| { + w.set_rxim(true); + w.set_rtim(true); + w.set_txim(true); + }); + } + + super::Uart::<'d, T, Async>::init(tx, rx, rts, cts, config); + + let state = T::state(); + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); +} + impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, @@ -48,17 +80,18 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx, rx); - Self::new_inner( + into_ref!(irq, tx, rx); + init::( irq, - tx.map_into(), - rx.map_into(), + Some(tx.map_into()), + Some(rx.map_into()), None, None, tx_buffer, rx_buffer, config, - ) + ); + Self { phantom: PhantomData } } pub fn new_with_rtscts( @@ -72,58 +105,17 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx, rx, cts, rts); - Self::new_inner( + into_ref!(irq, tx, rx, cts, rts); + init::( irq, - tx.map_into(), - rx.map_into(), + Some(tx.map_into()), + Some(rx.map_into()), Some(rts.map_into()), Some(cts.map_into()), tx_buffer, rx_buffer, config, - ) - } - - fn new_inner( - irq: impl Peripheral

+ 'd, - mut tx: PeripheralRef<'d, AnyPin>, - mut rx: PeripheralRef<'d, AnyPin>, - mut rts: Option>, - mut cts: Option>, - tx_buffer: &'d mut [u8], - rx_buffer: &'d mut [u8], - config: Config, - ) -> Self { - into_ref!(irq); - super::Uart::<'d, T, Async>::init( - Some(tx.reborrow()), - Some(rx.reborrow()), - rts.as_mut().map(|x| x.reborrow()), - cts.as_mut().map(|x| x.reborrow()), - config, ); - - let state = T::state(); - let regs = T::regs(); - - let len = tx_buffer.len(); - unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let len = rx_buffer.len(); - unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - - unsafe { - regs.uartimsc().modify(|w| { - w.set_rxim(true); - w.set_rtim(true); - w.set_txim(true); - }); - } - - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); - Self { phantom: PhantomData } } @@ -143,8 +135,9 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(rx); - Self::new_inner(irq, rx.map_into(), None, rx_buffer, config) + into_ref!(irq, rx); + init::(irq, None, Some(rx.map_into()), None, None, &mut [], rx_buffer, config); + Self { phantom: PhantomData } } pub fn new_with_rts( @@ -155,43 +148,17 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(rx, rts); - Self::new_inner(irq, rx.map_into(), Some(rts.map_into()), rx_buffer, config) - } - - fn new_inner( - irq: impl Peripheral

+ 'd, - mut rx: PeripheralRef<'d, AnyPin>, - mut rts: Option>, - rx_buffer: &'d mut [u8], - config: Config, - ) -> Self { - into_ref!(irq); - super::Uart::<'d, T, Async>::init( + into_ref!(irq, rx, rts); + init::( + irq, None, - Some(rx.reborrow()), - rts.as_mut().map(|x| x.reborrow()), + Some(rx.map_into()), + Some(rts.map_into()), None, + &mut [], + rx_buffer, config, ); - - let state = T::state(); - let regs = T::regs(); - - let len = rx_buffer.len(); - unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - - unsafe { - regs.uartimsc().modify(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } - - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); - Self { phantom: PhantomData } } @@ -231,7 +198,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn consume(amt: usize) { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; - rx_reader.pop_done(amt) + rx_reader.pop_done(amt); } } @@ -243,8 +210,9 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx); - Self::new_inner(irq, tx.map_into(), None, tx_buffer, config) + into_ref!(irq, tx); + init::(irq, Some(tx.map_into()), None, None, None, tx_buffer, &mut [], config); + Self { phantom: PhantomData } } pub fn new_with_cts( @@ -255,42 +223,17 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx, cts); - Self::new_inner(irq, tx.map_into(), Some(cts.map_into()), tx_buffer, config) - } - - fn new_inner( - irq: impl Peripheral

+ 'd, - mut tx: PeripheralRef<'d, AnyPin>, - mut cts: Option>, - tx_buffer: &'d mut [u8], - config: Config, - ) -> Self { - into_ref!(irq); - super::Uart::<'d, T, Async>::init( - Some(tx.reborrow()), + into_ref!(irq, tx, cts); + init::( + irq, + Some(tx.map_into()), None, None, - cts.as_mut().map(|x| x.reborrow()), + Some(cts.map_into()), + tx_buffer, + &mut [], config, ); - - let state = T::state(); - let regs = T::regs(); - - let len = tx_buffer.len(); - unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - - unsafe { - regs.uartimsc().modify(|w| { - w.set_txim(true); - }); - } - - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); - Self { phantom: PhantomData } } @@ -306,10 +249,9 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { if n == 0 { state.tx_waker.register(cx.waker()); return Poll::Pending; - } else { - unsafe { T::Interrupt::steal() }.pend(); } + unsafe { T::Interrupt::steal() }.pend(); Poll::Ready(Ok(n)) }) } From a24037edf9d04087111c1d1dc71c92cc0ad83709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 1 Jan 2023 22:02:45 +0100 Subject: [PATCH 264/374] rp: Fix BufferedUart drop code Only unregister the interrupt handler when both parts are inactive --- embassy-hal-common/src/atomic_ring_buffer.rs | 4 ++ embassy-rp/src/uart/buffered.rs | 51 ++++++++++---------- examples/rp/src/bin/uart_buffered_split.rs | 2 +- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index a8a6a216..4c944d76 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -81,6 +81,10 @@ impl RingBuffer { Writer(self) } + pub fn len(&self) -> usize { + self.len.load(Ordering::Relaxed) + } + pub fn is_full(&self) -> bool { let len = self.len.load(Ordering::Relaxed); let start = self.start.load(Ordering::Relaxed); diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index d853618a..9164794b 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -27,7 +27,8 @@ impl State { } pub struct BufferedUart<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, + rx: BufferedUartRx<'d, T>, + tx: BufferedUartTx<'d, T>, } pub struct BufferedUartRx<'d, T: Instance> { @@ -91,7 +92,10 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer, config, ); - Self { phantom: PhantomData } + Self { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, + } } pub fn new_with_rtscts( @@ -116,14 +120,14 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer, config, ); - Self { phantom: PhantomData } + Self { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, + } } - pub fn split(&mut self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { - ( - BufferedUartRx { phantom: PhantomData }, - BufferedUartTx { phantom: PhantomData }, - ) + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { + (self.rx, self.tx) } } @@ -269,35 +273,32 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> Drop for BufferedUart<'d, T> { - fn drop(&mut self) { - unsafe { - T::Interrupt::steal().disable(); - let state = T::state(); - state.tx_buf.deinit(); - state.rx_buf.deinit(); - } - } -} - impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { fn drop(&mut self) { + let state = T::state(); unsafe { - T::Interrupt::steal().disable(); - let state = T::state(); - state.tx_buf.deinit(); state.rx_buf.deinit(); + + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + T::Interrupt::steal().disable(); + } } } } impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { fn drop(&mut self) { + let state = T::state(); unsafe { - T::Interrupt::steal().disable(); - let state = T::state(); state.tx_buf.deinit(); - state.rx_buf.deinit(); + + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + T::Interrupt::steal().disable(); + } } } } diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 36f31c90..a8a68227 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -29,7 +29,7 @@ async fn main(spawner: Spawner) { let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut singleton!([0u8; 16])[..]; let rx_buf = &mut singleton!([0u8; 16])[..]; - let mut uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); + let uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); let (rx, mut tx) = uart.split(); unwrap!(spawner.spawn(reader(rx))); From 840a75674b632485d5b45cfece27870ded0edfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 4 Jan 2023 16:40:54 +0100 Subject: [PATCH 265/374] rp: Disable RX interrupts when ring buffer is full When data is in the RX fifo the RX timeout interrupt goes high again even after clearing it. The result is a deadlock because execution is stuck in the interrupt handler. No other code can run to clear the receive buffer. Enable and disable RX interrupts based on the buffer fill level. Use the same approach for the TX code path. --- embassy-rp/src/uart/buffered.rs | 87 +++++++++++++-------------------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9164794b..6465a20c 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -49,15 +49,6 @@ fn init<'d, T: Instance + 'd>( rx_buffer: &'d mut [u8], config: Config, ) { - let regs = T::regs(); - unsafe { - regs.uartimsc().modify(|w| { - w.set_rxim(true); - w.set_rtim(true); - w.set_txim(true); - }); - } - super::Uart::<'d, T, Async>::init(tx, rx, rts, cts, config); let state = T::state(); @@ -168,6 +159,8 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { poll_fn(move |cx| { + unsafe { T::Interrupt::steal() }.pend(); + let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { @@ -186,6 +179,8 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn fill_buf<'a>() -> impl Future> { poll_fn(move |cx| { + unsafe { T::Interrupt::steal() }.pend(); + let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); @@ -310,40 +305,31 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { let s = T::state(); unsafe { - // RX - let ris = r.uartris().read(); - // Clear interrupt flags - r.uarticr().write(|w| { - w.set_rxic(true); - w.set_rtic(true); - }); + let mut mis = r.uartimsc().read(); - if ris.peris() { - warn!("Parity error"); - r.uarticr().write(|w| { - w.set_peic(true); - }); - } + // Errors if ris.feris() { warn!("Framing error"); - r.uarticr().write(|w| { - w.set_feic(true); - }); + } + if ris.peris() { + warn!("Parity error"); } if ris.beris() { warn!("Break error"); - r.uarticr().write(|w| { - w.set_beic(true); - }); } if ris.oeris() { warn!("Overrun error"); - r.uarticr().write(|w| { - w.set_oeic(true); - }); } + // Clear any error flags + r.uarticr().write(|w| { + w.set_feic(true); + w.set_peic(true); + w.set_beic(true); + w.set_oeic(true); + }); + // RX let mut rx_writer = s.rx_buf.writer(); let rx_buf = rx_writer.push_slice(); let mut n_read = 0; @@ -358,33 +344,30 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { rx_writer.push_done(n_read); s.rx_waker.wake(); } + // Disable any further RX interrupts when the buffer becomes full. + mis.set_rxim(!s.rx_buf.is_full()); + mis.set_rtim(!s.rx_buf.is_full()); // TX let mut tx_reader = s.tx_buf.reader(); let tx_buf = tx_reader.pop_slice(); - if tx_buf.len() == 0 { - // Disable interrupt until we have something to transmit again - r.uartimsc().modify(|w| { - w.set_txim(false); - }); - } else { - r.uartimsc().modify(|w| { - w.set_txim(true); - }); - - let mut n_written = 0; - for tx_byte in tx_buf.iter_mut() { - if r.uartfr().read().txff() { - break; - } - r.uartdr().write(|w| w.set_data(*tx_byte)); - n_written += 1; - } - if n_written > 0 { - tx_reader.pop_done(n_written); - s.tx_waker.wake(); + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } + // Disable the TX interrupt when we do not have more data to send. + mis.set_txim(!s.tx_buf.is_empty()); + + // Update interrupt mask. + r.uartimsc().write_value(mis); } } From 6d4c6e0481f0738b8306172e7c5d54aa2758b74e Mon Sep 17 00:00:00 2001 From: Pedro Ferreira Date: Wed, 4 Jan 2023 20:27:07 +0100 Subject: [PATCH 266/374] rp2040: add {tx,rx}-only constructors to UART --- embassy-rp/src/uart/mod.rs | 32 ++++++++++++++++++++--- examples/rp/src/bin/uart_unidir.rs | 42 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 examples/rp/src/bin/uart_unidir.rs diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7e7bcaf3..bbbf97c0 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -98,7 +98,19 @@ pub struct UartRx<'d, T: Instance, M: Mode> { } impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { - fn new(tx_dma: Option>) -> Self { + /// Create a new DMA-enabled UART which can only send data + pub fn new( + _uart: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(tx, tx_dma); + Uart::::init(Some(tx.map_into()), None, None, None, config); + Self::new_inner(Some(tx_dma.map_into())) + } + + fn new_inner(tx_dma: Option>) -> Self { Self { tx_dma, phantom: PhantomData, @@ -140,7 +152,19 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { } impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { - fn new(rx_dma: Option>) -> Self { + /// Create a new DMA-enabled UART which can only send data + pub fn new( + _uart: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(rx, rx_dma); + Uart::::init(Some(rx.map_into()), None, None, None, config); + Self::new_inner(Some(rx_dma.map_into())) + } + + fn new_inner(rx_dma: Option>) -> Self { Self { rx_dma, phantom: PhantomData, @@ -295,8 +319,8 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { ); Self { - tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), + tx: UartTx::new_inner(tx_dma), + rx: UartRx::new_inner(rx_dma), } } diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs new file mode 100644 index 00000000..f56e7009 --- /dev/null +++ b/examples/rp/src/bin/uart_unidir.rs @@ -0,0 +1,42 @@ +//! test TX-only and RX-only UARTs. You need to connect GPIO0 to GPIO5 for +//! this to work + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::peripherals::UART1; +use embassy_rp::uart::{Async, Config, UartRx, UartTx}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); + let uart_rx = UartRx::new(p.UART1, p.PIN_5, p.DMA_CH1, Config::default()); + + unwrap!(spawner.spawn(reader(uart_rx))); + + info!("Writing..."); + loop { + let data = [1u8, 2, 3, 4, 5, 6, 7, 8]; + info!("TX {:?}", data); + uart_tx.write(&data).await.unwrap(); + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UartRx<'static, UART1, Async>) { + info!("Reading..."); + loop { + // read a total of 4 transmissions (32 / 8) and then print the result + let mut buf = [0; 32]; + rx.read(&mut buf).await.unwrap(); + info!("RX {:?}", buf); + } +} From 0ecc54f58c7a75bbaf89c875a23d500137dd52b6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 5 Jan 2023 01:46:35 +0100 Subject: [PATCH 267/374] usb/driver: document ControlPipe. --- embassy-usb-driver/src/lib.rs | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 9300ff81..71e5267c 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -215,6 +215,70 @@ pub trait EndpointOut: Endpoint { async fn read(&mut self, buf: &mut [u8]) -> Result; } +/// Trait for USB control pipe. +/// +/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single +/// unit, and manages them together to implement the control pipe state machine. +/// +/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that +/// many USB peripherals treat the control pipe endpoints differently (different registers, +/// different procedures), usually to accelerate processing in hardware somehow. A separate +/// trait allows the driver to handle it specially. +/// +/// The call sequences made by the USB stack to the ControlPipe are the following: +/// +/// - control in/out with len=0: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// accept() or reject() +/// ``` +/// +/// - control out with len != 0: +/// +/// ```not_rust +/// setup() +/// data_out(first=true, last=false) +/// data_out(first=false, last=false) +/// ... +/// data_out(first=false, last=false) +/// data_out(first=false, last=true) +/// (...processing...) +/// accept() or reject() +/// ``` +/// +/// - control in with len != 0, accepted: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// data_in(first=true, last=false) +/// data_in(first=false, last=false) +/// ... +/// data_in(first=false, last=false) +/// data_in(first=false, last=true) +/// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.) +/// ``` +/// +/// - control in with len != 0, rejected: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// reject() +/// ``` +/// +/// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length +/// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should +/// be triggered by either `accept()`, or `data_in` with `last = true`. +/// +/// Note that the host can abandon a control request and send a new STATUS packet any time. If +/// a STATUS packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, +/// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`) +/// to let the stack call `setup()` again to start handling the new control request. Not doing +/// so will cause things to get stuck, because the host will never read/send the packet we're +/// waiting for. pub trait ControlPipe { /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize; From 3c537a9fae5ed808a2efcc02a11cfdb8eebe0c6c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 5 Jan 2023 14:46:31 +0100 Subject: [PATCH 268/374] usb/driver: fix STATUS -> SETUP --- embassy-usb-driver/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 71e5267c..d7238dc7 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -273,8 +273,8 @@ pub trait EndpointOut: Endpoint { /// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should /// be triggered by either `accept()`, or `data_in` with `last = true`. /// -/// Note that the host can abandon a control request and send a new STATUS packet any time. If -/// a STATUS packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, +/// Note that the host can abandon a control request and send a new SETUP packet any time. If +/// a SETUP packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, /// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`) /// to let the stack call `setup()` again to start handling the new control request. Not doing /// so will cause things to get stuck, because the host will never read/send the packet we're From 1096a9746c0843e4a26fcec3fca7f9d6d9d57f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 5 Jan 2023 18:45:58 +0100 Subject: [PATCH 269/374] rp: Improve BufferedUart interrupt handling * Only clear interrupt flags that have fired (so that we do not lose any error flags) * Enable RX interrupt when a read is requested, disable it when the RX buffer is full * Rework TX interrupt handling: its "edge" triggered by a FIFO threshold --- embassy-rp/src/uart/buffered.rs | 79 ++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6465a20c..b9fc33ea 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -7,6 +7,7 @@ use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; +use crate::RegExt; pub struct State { tx_waker: AtomicWaker, @@ -57,6 +58,19 @@ fn init<'d, T: Instance + 'd>( let len = rx_buffer.len(); unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + // From the datasheet: + // "The transmit interrupt is based on a transition through a level, rather + // than on the level itself. When the interrupt and the UART is enabled + // before any data is written to the transmit FIFO the interrupt is not set. + // The interrupt is only set, after written data leaves the single location + // of the transmit FIFO and it becomes empty." + // + // This means we can leave the interrupt enabled the whole time as long as + // we clear it after it happens. The downside is that the we manually have + // to pend the ISR when we want data transmission to start. + let regs = T::regs(); + unsafe { regs.uartimsc().write_set(|w| w.set_txim(true)) }; + irq.set_handler(on_interrupt::); irq.unpend(); irq.enable(); @@ -159,8 +173,6 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { poll_fn(move |cx| { - unsafe { T::Interrupt::steal() }.pend(); - let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { @@ -173,14 +185,22 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { return Poll::Pending; } + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + let regs = T::regs(); + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } + Poll::Ready(Ok(n)) }) } fn fill_buf<'a>() -> impl Future> { poll_fn(move |cx| { - unsafe { T::Interrupt::steal() }.pend(); - let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); @@ -198,6 +218,16 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; rx_reader.pop_done(amt); + + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + let regs = T::regs(); + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } } } @@ -250,6 +280,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { return Poll::Pending; } + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. unsafe { T::Interrupt::steal() }.pend(); Poll::Ready(Ok(n)) }) @@ -299,14 +333,22 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { } pub(crate) unsafe fn on_interrupt(_: *mut ()) { - trace!("on_interrupt"); - let r = T::regs(); let s = T::state(); unsafe { + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by reading from the FIFO. let ris = r.uartris().read(); - let mut mis = r.uartimsc().read(); + r.uarticr().write(|w| { + w.set_txic(ris.txris()); + w.set_feic(ris.feris()); + w.set_peic(ris.peris()); + w.set_beic(ris.beris()); + w.set_oeic(ris.oeris()); + }); + + trace!("on_interrupt ris={=u32:#X}", ris.0); // Errors if ris.feris() { @@ -321,13 +363,6 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { if ris.oeris() { warn!("Overrun error"); } - // Clear any error flags - r.uarticr().write(|w| { - w.set_feic(true); - w.set_peic(true); - w.set_beic(true); - w.set_oeic(true); - }); // RX let mut rx_writer = s.rx_buf.writer(); @@ -345,8 +380,12 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { s.rx_waker.wake(); } // Disable any further RX interrupts when the buffer becomes full. - mis.set_rxim(!s.rx_buf.is_full()); - mis.set_rtim(!s.rx_buf.is_full()); + if s.rx_buf.is_full() { + r.uartimsc().write_clear(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } // TX let mut tx_reader = s.tx_buf.reader(); @@ -363,11 +402,9 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { tx_reader.pop_done(n_written); s.tx_waker.wake(); } - // Disable the TX interrupt when we do not have more data to send. - mis.set_txim(!s.tx_buf.is_empty()); - - // Update interrupt mask. - r.uartimsc().write_value(mis); + // The TX interrupt only triggers once when the FIFO threshold is + // crossed. No need to disable it when the buffer becomes empty + // as it does re-trigger anymore once we have cleared it. } } From 539f97da535edaf7607890658b21223ff6e02cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 5 Jan 2023 22:00:44 +0100 Subject: [PATCH 270/374] rp: Fix formatting string to please CI --- embassy-rp/src/uart/buffered.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index b9fc33ea..0da5bfca 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -348,7 +348,7 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { w.set_oeic(ris.oeris()); }); - trace!("on_interrupt ris={=u32:#X}", ris.0); + trace!("on_interrupt ris={:#X}", ris.0); // Errors if ris.feris() { From 4c4b47f78a57e5bf3d05b319759351a005e69206 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 6 Jan 2023 14:24:29 +0100 Subject: [PATCH 271/374] feat(stm32): Add embedded-io traits for UartRx and UartTx --- embassy-stm32/src/usart/mod.rs | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 233b56ba..1c13d9ec 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -948,6 +948,45 @@ mod eio { self.blocking_flush() } } + + impl Io for UartRx<'_, T, RxDma> + where + T: BasicInstance, + { + type Error = Error; + } + + impl Read for UartRx<'_, T, RxDma> + where + T: BasicInstance, + RxDma: super::RxDma, + { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_until_idle(buf).await + } + } + + impl Io for UartTx<'_, T, TxDma> + where + T: BasicInstance, + { + type Error = Error; + } + + impl Write for UartTx<'_, T, TxDma> + where + T: BasicInstance, + TxDma: super::TxDma, + { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } } #[cfg(all( From f8afc3c8828c334baaa399f73764ba45a3f69799 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Sun, 8 Jan 2023 15:36:35 +0100 Subject: [PATCH 272/374] Add samples for nrf5340 --- examples/nrf5340/.cargo/config.toml | 9 +++ examples/nrf5340/Cargo.toml | 64 ++++++++++++++++++ examples/nrf5340/build.rs | 35 ++++++++++ examples/nrf5340/memory.x | 7 ++ examples/nrf5340/src/bin/awaitable_timer.rs | 26 ++++++++ examples/nrf5340/src/bin/blinky.rs | 21 ++++++ examples/nrf5340/src/bin/buffered_uart.rs | 57 ++++++++++++++++ examples/nrf5340/src/bin/channel.rs | 43 ++++++++++++ .../src/bin/channel_sender_receiver.rs | 50 ++++++++++++++ examples/nrf5340/src/bin/gpiote_channel.rs | 66 +++++++++++++++++++ examples/nrf5340/src/bin/gpiote_port.rs | 34 ++++++++++ examples/nrf5340/src/bin/uart.rs | 35 ++++++++++ examples/nrf5340/src/bin/uart_idle.rs | 35 ++++++++++ examples/nrf5340/src/bin/uart_split.rs | 60 +++++++++++++++++ 14 files changed, 542 insertions(+) create mode 100644 examples/nrf5340/.cargo/config.toml create mode 100644 examples/nrf5340/Cargo.toml create mode 100644 examples/nrf5340/build.rs create mode 100644 examples/nrf5340/memory.x create mode 100644 examples/nrf5340/src/bin/awaitable_timer.rs create mode 100644 examples/nrf5340/src/bin/blinky.rs create mode 100644 examples/nrf5340/src/bin/buffered_uart.rs create mode 100644 examples/nrf5340/src/bin/channel.rs create mode 100644 examples/nrf5340/src/bin/channel_sender_receiver.rs create mode 100644 examples/nrf5340/src/bin/gpiote_channel.rs create mode 100644 examples/nrf5340/src/bin/gpiote_port.rs create mode 100644 examples/nrf5340/src/bin/uart.rs create mode 100644 examples/nrf5340/src/bin/uart_idle.rs create mode 100644 examples/nrf5340/src/bin/uart_split.rs diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml new file mode 100644 index 00000000..ff0879c8 --- /dev/null +++ b/examples/nrf5340/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF5340_xxAA with your chip as listed in `probe-run --list-chips` +runner = "probe-run --chip nRF5340_xxAA" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml new file mode 100644 index 00000000..03485711 --- /dev/null +++ b/examples/nrf5340/Cargo.toml @@ -0,0 +1,64 @@ +[package] +edition = "2021" +name = "embassy-nrf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[features] +default = ["nightly"] +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" } +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-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ + "defmt", + "nrf5340-app-s", + "time-driver-rtc1", + "gpiote", + "unstable-pac", +] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ + "defmt", + "tcp", + "dhcpv4", + "medium-ethernet", +], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ + "defmt", +], optional = true } +embedded-io = "0.4.0" + + +defmt = "0.3" +defmt-rtt = "0.4" + +static_cell = "1.0" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", +] } +rand = { version = "0.8.4", default-features = false } +embedded-storage = "0.3.0" +usbd-hid = "0.6.0" +serde = { version = "1.0.136", default-features = false } diff --git a/examples/nrf5340/build.rs b/examples/nrf5340/build.rs new file mode 100644 index 00000000..30691aa9 --- /dev/null +++ b/examples/nrf5340/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/nrf5340/memory.x b/examples/nrf5340/memory.x new file mode 100644 index 00000000..a122dc24 --- /dev/null +++ b/examples/nrf5340/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF5340 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/nrf5340/src/bin/awaitable_timer.rs b/examples/nrf5340/src/bin/awaitable_timer.rs new file mode 100644 index 00000000..b32af236 --- /dev/null +++ b/examples/nrf5340/src/bin/awaitable_timer.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::timer::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); + // default frequency is 1MHz, so this triggers every second + t.cc(0).write(1_000_000); + // clear the timer value on cc[0] compare match + t.cc(0).short_compare_clear(); + t.start(); + + loop { + // wait for compare match + t.cc(0).wait().await; + info!("hardware timer tick"); + } +} diff --git a/examples/nrf5340/src/bin/blinky.rs b/examples/nrf5340/src/bin/blinky.rs new file mode 100644 index 00000000..3422cedf --- /dev/null +++ b/examples/nrf5340/src/bin/blinky.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} diff --git a/examples/nrf5340/src/bin/buffered_uart.rs b/examples/nrf5340/src/bin/buffered_uart.rs new file mode 100644 index 00000000..25a0ca23 --- /dev/null +++ b/examples/nrf5340/src/bin/buffered_uart.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::buffered_uarte::{BufferedUarte, State}; +use embassy_nrf::{interrupt, uarte}; +use embedded_io::asynch::{BufRead, Write}; +use futures::pin_mut; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let mut tx_buffer = [0u8; 4096]; + let mut rx_buffer = [0u8; 4096]; + + let irq = interrupt::take!(SERIAL0); + let mut state = State::new(); + // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) + let u = BufferedUarte::new( + &mut state, + p.UARTETWISPI0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + irq, + p.P0_08, + p.P0_06, + p.P0_07, + p.P0_05, + config, + &mut rx_buffer, + &mut tx_buffer, + ); + pin_mut!(u); + + info!("uarte initialized!"); + + unwrap!(u.write_all(b"Hello!\r\n").await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let buf = unwrap!(u.fill_buf().await); + info!("read done, got {}", buf); + + // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again + let n = buf.len(); + u.consume(n); + } +} diff --git a/examples/nrf5340/src/bin/channel.rs b/examples/nrf5340/src/bin/channel.rs new file mode 100644 index 00000000..425d4305 --- /dev/null +++ b/examples/nrf5340/src/bin/channel.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +enum LedState { + On, + Off, +} + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::task] +async fn my_task() { + loop { + CHANNEL.send(LedState::On).await; + Timer::after(Duration::from_secs(1)).await; + CHANNEL.send(LedState::Off).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); + + unwrap!(spawner.spawn(my_task())); + + loop { + match CHANNEL.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} diff --git a/examples/nrf5340/src/bin/channel_sender_receiver.rs b/examples/nrf5340/src/bin/channel_sender_receiver.rs new file mode 100644 index 00000000..9628c052 --- /dev/null +++ b/examples/nrf5340/src/bin/channel_sender_receiver.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::channel::{Channel, Receiver, Sender}; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +enum LedState { + On, + Off, +} + +static CHANNEL: StaticCell> = StaticCell::new(); + +#[embassy_executor::task] +async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { + loop { + sender.send(LedState::On).await; + Timer::after(Duration::from_secs(1)).await; + sender.send(LedState::Off).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { + let mut led = Output::new(led, Level::Low, OutputDrive::Standard); + + loop { + match receiver.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let channel = CHANNEL.init(Channel::new()); + + unwrap!(spawner.spawn(send_task(channel.sender()))); + unwrap!(spawner.spawn(recv_task(p.P0_28.degrade(), channel.receiver()))); +} diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs new file mode 100644 index 00000000..ceab1194 --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_channel.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Input, Pull}; +use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Starting!"); + + let ch1 = InputChannel::new( + p.GPIOTE_CH0, + Input::new(p.P0_23, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let ch2 = InputChannel::new( + p.GPIOTE_CH1, + Input::new(p.P0_24, Pull::Up), + InputChannelPolarity::LoToHi, + ); + let ch3 = InputChannel::new( + p.GPIOTE_CH2, + Input::new(p.P0_08, Pull::Up), + InputChannelPolarity::Toggle, + ); + let ch4 = InputChannel::new( + p.GPIOTE_CH3, + Input::new(p.P0_09, Pull::Up), + InputChannelPolarity::Toggle, + ); + + let button1 = async { + loop { + ch1.wait().await; + info!("Button 1 pressed") + } + }; + + let button2 = async { + loop { + ch2.wait().await; + info!("Button 2 released") + } + }; + + let button3 = async { + loop { + ch3.wait().await; + info!("Button 3 toggled") + } + }; + + let button4 = async { + loop { + ch4.wait().await; + info!("Button 4 toggled") + } + }; + + futures::join!(button1, button2, button3, button4); +} diff --git a/examples/nrf5340/src/bin/gpiote_port.rs b/examples/nrf5340/src/bin/gpiote_port.rs new file mode 100644 index 00000000..0cc911ad --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_port.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task(pool_size = 4)] +async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { + loop { + pin.wait_for_low().await; + info!("Button {:?} pressed!", n); + pin.wait_for_high().await; + info!("Button {:?} released!", n); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Starting!"); + + let btn1 = Input::new(p.P0_23.degrade(), Pull::Up); + let btn2 = Input::new(p.P0_24.degrade(), Pull::Up); + let btn3 = Input::new(p.P0_08.degrade(), Pull::Up); + let btn4 = Input::new(p.P0_09.degrade(), Pull::Up); + + unwrap!(spawner.spawn(button_task(1, btn1))); + unwrap!(spawner.spawn(button_task(2, btn2))); + unwrap!(spawner.spawn(button_task(3, btn3))); + unwrap!(spawner.spawn(button_task(4, btn4))); +} diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs new file mode 100644 index 00000000..1db45057 --- /dev/null +++ b/examples/nrf5340/src/bin/uart.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(uart.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + unwrap!(uart.read(&mut buf).await); + info!("writing..."); + unwrap!(uart.write(&buf).await); + } +} diff --git a/examples/nrf5340/src/bin/uart_idle.rs b/examples/nrf5340/src/bin/uart_idle.rs new file mode 100644 index 00000000..327fc4b2 --- /dev/null +++ b/examples/nrf5340/src/bin/uart_idle.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(tx.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let n = unwrap!(rx.read_until_idle(&mut buf).await); + info!("got {} bytes", n); + } +} diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs new file mode 100644 index 00000000..1bff382f --- /dev/null +++ b/examples/nrf5340/src/bin/uart_split.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::peripherals::UARTETWISPI0; +use embassy_nrf::uarte::UarteRx; +use embassy_nrf::{interrupt, uarte}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let (mut tx, rx) = uart.split(); + + info!("uarte initialized!"); + + // Spawn a task responsible purely for reading + + unwrap!(spawner.spawn(reader(rx))); + + // Message must be in SRAM + { + let mut buf = [0; 23]; + buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); + + unwrap!(tx.write(&buf).await); + info!("wrote hello in uart!"); + } + + // Continue reading in this main task and write + // back out the buffer we receive from the read + // task. + loop { + let buf = CHANNEL.recv().await; + info!("writing..."); + unwrap!(tx.write(&buf).await); + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UarteRx<'static, UARTETWISPI0>) { + let mut buf = [0; 8]; + loop { + info!("reading..."); + unwrap!(rx.read(&mut buf).await); + CHANNEL.send(buf).await; + } +} From 401185b1d95a2519ee94e5d5654cc9325fe85eec Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Sun, 8 Jan 2023 16:25:51 +0100 Subject: [PATCH 273/374] Change UART pins for nRF5340 DK --- examples/nrf5340/src/bin/uart.rs | 2 +- examples/nrf5340/src/bin/uart_split.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 1db45057..0f2b7b1e 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(SERIAL0); - let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); info!("uarte initialized!"); diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs index 1bff382f..0bbbfeaa 100644 --- a/examples/nrf5340/src/bin/uart_split.rs +++ b/examples/nrf5340/src/bin/uart_split.rs @@ -21,7 +21,7 @@ async fn main(spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(SERIAL0); - let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); let (mut tx, rx) = uart.split(); info!("uarte initialized!"); From 0a27b6cedb52453123190671f294bbd34918e09a Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Mon, 9 Jan 2023 22:29:58 +0100 Subject: [PATCH 274/374] Rename examples/nrf to examples/nrf52840 --- README.md | 7 ++++--- ci.sh | 3 ++- ci_stable.sh | 2 +- docs/modules/ROOT/pages/getting_started.adoc | 2 +- examples/{nrf => nrf52840}/.cargo/config.toml | 0 examples/{nrf => nrf52840}/Cargo.toml | 0 examples/{nrf => nrf52840}/build.rs | 0 examples/{nrf => nrf52840}/memory.x | 0 examples/{nrf => nrf52840}/src/bin/awaitable_timer.rs | 0 examples/{nrf => nrf52840}/src/bin/blinky.rs | 0 examples/{nrf => nrf52840}/src/bin/buffered_uart.rs | 0 examples/{nrf => nrf52840}/src/bin/channel.rs | 0 .../{nrf => nrf52840}/src/bin/channel_sender_receiver.rs | 0 .../{nrf => nrf52840}/src/bin/executor_fairness_test.rs | 0 examples/{nrf => nrf52840}/src/bin/gpiote_channel.rs | 0 examples/{nrf => nrf52840}/src/bin/gpiote_port.rs | 0 examples/{nrf => nrf52840}/src/bin/i2s_effect.rs | 0 examples/{nrf => nrf52840}/src/bin/i2s_monitor.rs | 0 examples/{nrf => nrf52840}/src/bin/i2s_waveform.rs | 0 examples/{nrf => nrf52840}/src/bin/lora_p2p_report.rs | 0 examples/{nrf => nrf52840}/src/bin/lora_p2p_sense.rs | 0 .../{nrf => nrf52840}/src/bin/manually_create_executor.rs | 0 examples/{nrf => nrf52840}/src/bin/multiprio.rs | 0 examples/{nrf => nrf52840}/src/bin/mutex.rs | 0 examples/{nrf => nrf52840}/src/bin/nvmc.rs | 0 examples/{nrf => nrf52840}/src/bin/pdm.rs | 0 examples/{nrf => nrf52840}/src/bin/ppi.rs | 0 examples/{nrf => nrf52840}/src/bin/pubsub.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_double_sequence.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_sequence.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_sequence_ppi.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_sequence_ws2812b.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_servo.rs | 0 examples/{nrf => nrf52840}/src/bin/qdec.rs | 0 examples/{nrf => nrf52840}/src/bin/qspi.rs | 0 examples/{nrf => nrf52840}/src/bin/qspi_lowpower.rs | 0 examples/{nrf => nrf52840}/src/bin/raw_spawn.rs | 0 examples/{nrf => nrf52840}/src/bin/rng.rs | 0 examples/{nrf => nrf52840}/src/bin/saadc.rs | 0 examples/{nrf => nrf52840}/src/bin/saadc_continuous.rs | 0 examples/{nrf => nrf52840}/src/bin/self_spawn.rs | 0 .../src/bin/self_spawn_current_executor.rs | 0 examples/{nrf => nrf52840}/src/bin/spim.rs | 0 examples/{nrf => nrf52840}/src/bin/spis.rs | 0 examples/{nrf => nrf52840}/src/bin/temp.rs | 0 examples/{nrf => nrf52840}/src/bin/timer.rs | 0 examples/{nrf => nrf52840}/src/bin/twim.rs | 0 examples/{nrf => nrf52840}/src/bin/twim_lowpower.rs | 0 examples/{nrf => nrf52840}/src/bin/twis.rs | 0 examples/{nrf => nrf52840}/src/bin/uart.rs | 0 examples/{nrf => nrf52840}/src/bin/uart_idle.rs | 0 examples/{nrf => nrf52840}/src/bin/uart_split.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_ethernet.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_hid_keyboard.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_hid_mouse.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_serial.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_serial_multitask.rs | 0 examples/{nrf => nrf52840}/src/bin/wdt.rs | 0 59 files changed, 8 insertions(+), 6 deletions(-) rename examples/{nrf => nrf52840}/.cargo/config.toml (100%) rename examples/{nrf => nrf52840}/Cargo.toml (100%) rename examples/{nrf => nrf52840}/build.rs (100%) rename examples/{nrf => nrf52840}/memory.x (100%) rename examples/{nrf => nrf52840}/src/bin/awaitable_timer.rs (100%) rename examples/{nrf => nrf52840}/src/bin/blinky.rs (100%) rename examples/{nrf => nrf52840}/src/bin/buffered_uart.rs (100%) rename examples/{nrf => nrf52840}/src/bin/channel.rs (100%) rename examples/{nrf => nrf52840}/src/bin/channel_sender_receiver.rs (100%) rename examples/{nrf => nrf52840}/src/bin/executor_fairness_test.rs (100%) rename examples/{nrf => nrf52840}/src/bin/gpiote_channel.rs (100%) rename examples/{nrf => nrf52840}/src/bin/gpiote_port.rs (100%) rename examples/{nrf => nrf52840}/src/bin/i2s_effect.rs (100%) rename examples/{nrf => nrf52840}/src/bin/i2s_monitor.rs (100%) rename examples/{nrf => nrf52840}/src/bin/i2s_waveform.rs (100%) rename examples/{nrf => nrf52840}/src/bin/lora_p2p_report.rs (100%) rename examples/{nrf => nrf52840}/src/bin/lora_p2p_sense.rs (100%) rename examples/{nrf => nrf52840}/src/bin/manually_create_executor.rs (100%) rename examples/{nrf => nrf52840}/src/bin/multiprio.rs (100%) rename examples/{nrf => nrf52840}/src/bin/mutex.rs (100%) rename examples/{nrf => nrf52840}/src/bin/nvmc.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pdm.rs (100%) rename examples/{nrf => nrf52840}/src/bin/ppi.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pubsub.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_double_sequence.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_sequence.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_sequence_ppi.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_sequence_ws2812b.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_servo.rs (100%) rename examples/{nrf => nrf52840}/src/bin/qdec.rs (100%) rename examples/{nrf => nrf52840}/src/bin/qspi.rs (100%) rename examples/{nrf => nrf52840}/src/bin/qspi_lowpower.rs (100%) rename examples/{nrf => nrf52840}/src/bin/raw_spawn.rs (100%) rename examples/{nrf => nrf52840}/src/bin/rng.rs (100%) rename examples/{nrf => nrf52840}/src/bin/saadc.rs (100%) rename examples/{nrf => nrf52840}/src/bin/saadc_continuous.rs (100%) rename examples/{nrf => nrf52840}/src/bin/self_spawn.rs (100%) rename examples/{nrf => nrf52840}/src/bin/self_spawn_current_executor.rs (100%) rename examples/{nrf => nrf52840}/src/bin/spim.rs (100%) rename examples/{nrf => nrf52840}/src/bin/spis.rs (100%) rename examples/{nrf => nrf52840}/src/bin/temp.rs (100%) rename examples/{nrf => nrf52840}/src/bin/timer.rs (100%) rename examples/{nrf => nrf52840}/src/bin/twim.rs (100%) rename examples/{nrf => nrf52840}/src/bin/twim_lowpower.rs (100%) rename examples/{nrf => nrf52840}/src/bin/twis.rs (100%) rename examples/{nrf => nrf52840}/src/bin/uart.rs (100%) rename examples/{nrf => nrf52840}/src/bin/uart_idle.rs (100%) rename examples/{nrf => nrf52840}/src/bin/uart_split.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_ethernet.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_hid_keyboard.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_hid_mouse.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_serial.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_serial_multitask.rs (100%) rename examples/{nrf => nrf52840}/src/bin/wdt.rs (100%) diff --git a/README.md b/README.md index eaa91012..938f2f4a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Rust's async/await allows No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. - **Real-time ready** - -Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. +Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. - **Low-power ready** - Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting. @@ -87,7 +87,8 @@ async fn main(spawner: Spawner) { Examples are found in the `examples/` folder seperated by the chip manufacturer they are designed to run on. For example: -* `examples/nrf` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. +* `examples/nrf52840` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. +* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). * `examples/stm32xx` for the various STM32 families. * `examples/rp` are for the RP2040 chip. * `examples/std` are designed to run locally on your PC. @@ -110,7 +111,7 @@ cargo install probe-run - Change directory to the sample's base directory. For example: ```bash -cd examples/nrf +cd examples/nrf52840 ``` - Run the example diff --git a/ci.sh b/ci.sh index f59f3f46..30e664a2 100755 --- a/ci.sh +++ b/ci.sh @@ -89,7 +89,8 @@ cargo batch \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf \ + --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \ + --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \ --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \ --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \ --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ diff --git a/ci_stable.sh b/ci_stable.sh index a1d507d7..60ddb659 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -65,5 +65,5 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \ + --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \ diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index f3492a3d..9015d784 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc @@ -45,7 +45,7 @@ You can run an example by opening a terminal and entering the following commands [source, bash] ---- -cd examples/nrf +cd examples/nrf52840 cargo run --bin blinky --release ---- diff --git a/examples/nrf/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml similarity index 100% rename from examples/nrf/.cargo/config.toml rename to examples/nrf52840/.cargo/config.toml diff --git a/examples/nrf/Cargo.toml b/examples/nrf52840/Cargo.toml similarity index 100% rename from examples/nrf/Cargo.toml rename to examples/nrf52840/Cargo.toml diff --git a/examples/nrf/build.rs b/examples/nrf52840/build.rs similarity index 100% rename from examples/nrf/build.rs rename to examples/nrf52840/build.rs diff --git a/examples/nrf/memory.x b/examples/nrf52840/memory.x similarity index 100% rename from examples/nrf/memory.x rename to examples/nrf52840/memory.x diff --git a/examples/nrf/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs similarity index 100% rename from examples/nrf/src/bin/awaitable_timer.rs rename to examples/nrf52840/src/bin/awaitable_timer.rs diff --git a/examples/nrf/src/bin/blinky.rs b/examples/nrf52840/src/bin/blinky.rs similarity index 100% rename from examples/nrf/src/bin/blinky.rs rename to examples/nrf52840/src/bin/blinky.rs diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs similarity index 100% rename from examples/nrf/src/bin/buffered_uart.rs rename to examples/nrf52840/src/bin/buffered_uart.rs diff --git a/examples/nrf/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs similarity index 100% rename from examples/nrf/src/bin/channel.rs rename to examples/nrf52840/src/bin/channel.rs diff --git a/examples/nrf/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs similarity index 100% rename from examples/nrf/src/bin/channel_sender_receiver.rs rename to examples/nrf52840/src/bin/channel_sender_receiver.rs diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf52840/src/bin/executor_fairness_test.rs similarity index 100% rename from examples/nrf/src/bin/executor_fairness_test.rs rename to examples/nrf52840/src/bin/executor_fairness_test.rs diff --git a/examples/nrf/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs similarity index 100% rename from examples/nrf/src/bin/gpiote_channel.rs rename to examples/nrf52840/src/bin/gpiote_channel.rs diff --git a/examples/nrf/src/bin/gpiote_port.rs b/examples/nrf52840/src/bin/gpiote_port.rs similarity index 100% rename from examples/nrf/src/bin/gpiote_port.rs rename to examples/nrf52840/src/bin/gpiote_port.rs diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs similarity index 100% rename from examples/nrf/src/bin/i2s_effect.rs rename to examples/nrf52840/src/bin/i2s_effect.rs diff --git a/examples/nrf/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs similarity index 100% rename from examples/nrf/src/bin/i2s_monitor.rs rename to examples/nrf52840/src/bin/i2s_monitor.rs diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs similarity index 100% rename from examples/nrf/src/bin/i2s_waveform.rs rename to examples/nrf52840/src/bin/i2s_waveform.rs diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs similarity index 100% rename from examples/nrf/src/bin/lora_p2p_report.rs rename to examples/nrf52840/src/bin/lora_p2p_report.rs diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs similarity index 100% rename from examples/nrf/src/bin/lora_p2p_sense.rs rename to examples/nrf52840/src/bin/lora_p2p_sense.rs diff --git a/examples/nrf/src/bin/manually_create_executor.rs b/examples/nrf52840/src/bin/manually_create_executor.rs similarity index 100% rename from examples/nrf/src/bin/manually_create_executor.rs rename to examples/nrf52840/src/bin/manually_create_executor.rs diff --git a/examples/nrf/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs similarity index 100% rename from examples/nrf/src/bin/multiprio.rs rename to examples/nrf52840/src/bin/multiprio.rs diff --git a/examples/nrf/src/bin/mutex.rs b/examples/nrf52840/src/bin/mutex.rs similarity index 100% rename from examples/nrf/src/bin/mutex.rs rename to examples/nrf52840/src/bin/mutex.rs diff --git a/examples/nrf/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs similarity index 100% rename from examples/nrf/src/bin/nvmc.rs rename to examples/nrf52840/src/bin/nvmc.rs diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs similarity index 100% rename from examples/nrf/src/bin/pdm.rs rename to examples/nrf52840/src/bin/pdm.rs diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf52840/src/bin/ppi.rs similarity index 100% rename from examples/nrf/src/bin/ppi.rs rename to examples/nrf52840/src/bin/ppi.rs diff --git a/examples/nrf/src/bin/pubsub.rs b/examples/nrf52840/src/bin/pubsub.rs similarity index 100% rename from examples/nrf/src/bin/pubsub.rs rename to examples/nrf52840/src/bin/pubsub.rs diff --git a/examples/nrf/src/bin/pwm.rs b/examples/nrf52840/src/bin/pwm.rs similarity index 100% rename from examples/nrf/src/bin/pwm.rs rename to examples/nrf52840/src/bin/pwm.rs diff --git a/examples/nrf/src/bin/pwm_double_sequence.rs b/examples/nrf52840/src/bin/pwm_double_sequence.rs similarity index 100% rename from examples/nrf/src/bin/pwm_double_sequence.rs rename to examples/nrf52840/src/bin/pwm_double_sequence.rs diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf52840/src/bin/pwm_sequence.rs similarity index 100% rename from examples/nrf/src/bin/pwm_sequence.rs rename to examples/nrf52840/src/bin/pwm_sequence.rs diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf52840/src/bin/pwm_sequence_ppi.rs similarity index 100% rename from examples/nrf/src/bin/pwm_sequence_ppi.rs rename to examples/nrf52840/src/bin/pwm_sequence_ppi.rs diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs similarity index 100% rename from examples/nrf/src/bin/pwm_sequence_ws2812b.rs rename to examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs diff --git a/examples/nrf/src/bin/pwm_servo.rs b/examples/nrf52840/src/bin/pwm_servo.rs similarity index 100% rename from examples/nrf/src/bin/pwm_servo.rs rename to examples/nrf52840/src/bin/pwm_servo.rs diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs similarity index 100% rename from examples/nrf/src/bin/qdec.rs rename to examples/nrf52840/src/bin/qdec.rs diff --git a/examples/nrf/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs similarity index 100% rename from examples/nrf/src/bin/qspi.rs rename to examples/nrf52840/src/bin/qspi.rs diff --git a/examples/nrf/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs similarity index 100% rename from examples/nrf/src/bin/qspi_lowpower.rs rename to examples/nrf52840/src/bin/qspi_lowpower.rs diff --git a/examples/nrf/src/bin/raw_spawn.rs b/examples/nrf52840/src/bin/raw_spawn.rs similarity index 100% rename from examples/nrf/src/bin/raw_spawn.rs rename to examples/nrf52840/src/bin/raw_spawn.rs diff --git a/examples/nrf/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs similarity index 100% rename from examples/nrf/src/bin/rng.rs rename to examples/nrf52840/src/bin/rng.rs diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs similarity index 100% rename from examples/nrf/src/bin/saadc.rs rename to examples/nrf52840/src/bin/saadc.rs diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs similarity index 100% rename from examples/nrf/src/bin/saadc_continuous.rs rename to examples/nrf52840/src/bin/saadc_continuous.rs diff --git a/examples/nrf/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs similarity index 100% rename from examples/nrf/src/bin/self_spawn.rs rename to examples/nrf52840/src/bin/self_spawn.rs diff --git a/examples/nrf/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs similarity index 100% rename from examples/nrf/src/bin/self_spawn_current_executor.rs rename to examples/nrf52840/src/bin/self_spawn_current_executor.rs diff --git a/examples/nrf/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs similarity index 100% rename from examples/nrf/src/bin/spim.rs rename to examples/nrf52840/src/bin/spim.rs diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs similarity index 100% rename from examples/nrf/src/bin/spis.rs rename to examples/nrf52840/src/bin/spis.rs diff --git a/examples/nrf/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs similarity index 100% rename from examples/nrf/src/bin/temp.rs rename to examples/nrf52840/src/bin/temp.rs diff --git a/examples/nrf/src/bin/timer.rs b/examples/nrf52840/src/bin/timer.rs similarity index 100% rename from examples/nrf/src/bin/timer.rs rename to examples/nrf52840/src/bin/timer.rs diff --git a/examples/nrf/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs similarity index 100% rename from examples/nrf/src/bin/twim.rs rename to examples/nrf52840/src/bin/twim.rs diff --git a/examples/nrf/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs similarity index 100% rename from examples/nrf/src/bin/twim_lowpower.rs rename to examples/nrf52840/src/bin/twim_lowpower.rs diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs similarity index 100% rename from examples/nrf/src/bin/twis.rs rename to examples/nrf52840/src/bin/twis.rs diff --git a/examples/nrf/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs similarity index 100% rename from examples/nrf/src/bin/uart.rs rename to examples/nrf52840/src/bin/uart.rs diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs similarity index 100% rename from examples/nrf/src/bin/uart_idle.rs rename to examples/nrf52840/src/bin/uart_idle.rs diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs similarity index 100% rename from examples/nrf/src/bin/uart_split.rs rename to examples/nrf52840/src/bin/uart_split.rs diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs similarity index 100% rename from examples/nrf/src/bin/usb_ethernet.rs rename to examples/nrf52840/src/bin/usb_ethernet.rs diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs similarity index 100% rename from examples/nrf/src/bin/usb_hid_keyboard.rs rename to examples/nrf52840/src/bin/usb_hid_keyboard.rs diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs similarity index 100% rename from examples/nrf/src/bin/usb_hid_mouse.rs rename to examples/nrf52840/src/bin/usb_hid_mouse.rs diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs similarity index 100% rename from examples/nrf/src/bin/usb_serial.rs rename to examples/nrf52840/src/bin/usb_serial.rs diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs similarity index 100% rename from examples/nrf/src/bin/usb_serial_multitask.rs rename to examples/nrf52840/src/bin/usb_serial_multitask.rs diff --git a/examples/nrf/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs similarity index 100% rename from examples/nrf/src/bin/wdt.rs rename to examples/nrf52840/src/bin/wdt.rs From 2baebabf4dd2abecfd08ca078ecf59060d5ad585 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Mon, 9 Jan 2023 22:57:40 +0100 Subject: [PATCH 275/374] Reduce amount of samples for nrf5340 --- .vscode/settings.json | 3 +- examples/nrf5340/src/bin/awaitable_timer.rs | 26 -------- examples/nrf5340/src/bin/buffered_uart.rs | 57 ------------------ examples/nrf5340/src/bin/channel.rs | 43 ------------- .../src/bin/channel_sender_receiver.rs | 50 ---------------- examples/nrf5340/src/bin/gpiote_port.rs | 34 ----------- examples/nrf5340/src/bin/uart_idle.rs | 35 ----------- examples/nrf5340/src/bin/uart_split.rs | 60 ------------------- 8 files changed, 2 insertions(+), 306 deletions(-) delete mode 100644 examples/nrf5340/src/bin/awaitable_timer.rs delete mode 100644 examples/nrf5340/src/bin/buffered_uart.rs delete mode 100644 examples/nrf5340/src/bin/channel.rs delete mode 100644 examples/nrf5340/src/bin/channel_sender_receiver.rs delete mode 100644 examples/nrf5340/src/bin/gpiote_port.rs delete mode 100644 examples/nrf5340/src/bin/uart_idle.rs delete mode 100644 examples/nrf5340/src/bin/uart_split.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 086f435d..402ed241 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,8 @@ // Declare for the target you wish to develop // "embassy-executor/Cargo.toml", // "embassy-sync/Cargo.toml", - "examples/nrf/Cargo.toml", + "examples/nrf52840/Cargo.toml", + //"examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", diff --git a/examples/nrf5340/src/bin/awaitable_timer.rs b/examples/nrf5340/src/bin/awaitable_timer.rs deleted file mode 100644 index b32af236..00000000 --- a/examples/nrf5340/src/bin/awaitable_timer.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::info; -use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::timer::Timer; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); - // default frequency is 1MHz, so this triggers every second - t.cc(0).write(1_000_000); - // clear the timer value on cc[0] compare match - t.cc(0).short_compare_clear(); - t.start(); - - loop { - // wait for compare match - t.cc(0).wait().await; - info!("hardware timer tick"); - } -} diff --git a/examples/nrf5340/src/bin/buffered_uart.rs b/examples/nrf5340/src/bin/buffered_uart.rs deleted file mode 100644 index 25a0ca23..00000000 --- a/examples/nrf5340/src/bin/buffered_uart.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::{BufferedUarte, State}; -use embassy_nrf::{interrupt, uarte}; -use embedded_io::asynch::{BufRead, Write}; -use futures::pin_mut; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut config = uarte::Config::default(); - config.parity = uarte::Parity::EXCLUDED; - config.baudrate = uarte::Baudrate::BAUD115200; - - let mut tx_buffer = [0u8; 4096]; - let mut rx_buffer = [0u8; 4096]; - - let irq = interrupt::take!(SERIAL0); - let mut state = State::new(); - // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) - let u = BufferedUarte::new( - &mut state, - p.UARTETWISPI0, - p.TIMER0, - p.PPI_CH0, - p.PPI_CH1, - irq, - p.P0_08, - p.P0_06, - p.P0_07, - p.P0_05, - config, - &mut rx_buffer, - &mut tx_buffer, - ); - pin_mut!(u); - - info!("uarte initialized!"); - - unwrap!(u.write_all(b"Hello!\r\n").await); - info!("wrote hello in uart!"); - - loop { - info!("reading..."); - let buf = unwrap!(u.fill_buf().await); - info!("read done, got {}", buf); - - // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again - let n = buf.len(); - u.consume(n); - } -} diff --git a/examples/nrf5340/src/bin/channel.rs b/examples/nrf5340/src/bin/channel.rs deleted file mode 100644 index 425d4305..00000000 --- a/examples/nrf5340/src/bin/channel.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::unwrap; -use embassy_executor::Spawner; -use embassy_nrf::gpio::{Level, Output, OutputDrive}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use embassy_time::{Duration, Timer}; -use {defmt_rtt as _, panic_probe as _}; - -enum LedState { - On, - Off, -} - -static CHANNEL: Channel = Channel::new(); - -#[embassy_executor::task] -async fn my_task() { - loop { - CHANNEL.send(LedState::On).await; - Timer::after(Duration::from_secs(1)).await; - CHANNEL.send(LedState::Off).await; - Timer::after(Duration::from_secs(1)).await; - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); - - unwrap!(spawner.spawn(my_task())); - - loop { - match CHANNEL.recv().await { - LedState::On => led.set_high(), - LedState::Off => led.set_low(), - } - } -} diff --git a/examples/nrf5340/src/bin/channel_sender_receiver.rs b/examples/nrf5340/src/bin/channel_sender_receiver.rs deleted file mode 100644 index 9628c052..00000000 --- a/examples/nrf5340/src/bin/channel_sender_receiver.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::unwrap; -use embassy_executor::Spawner; -use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::channel::{Channel, Receiver, Sender}; -use embassy_time::{Duration, Timer}; -use static_cell::StaticCell; -use {defmt_rtt as _, panic_probe as _}; - -enum LedState { - On, - Off, -} - -static CHANNEL: StaticCell> = StaticCell::new(); - -#[embassy_executor::task] -async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { - loop { - sender.send(LedState::On).await; - Timer::after(Duration::from_secs(1)).await; - sender.send(LedState::Off).await; - Timer::after(Duration::from_secs(1)).await; - } -} - -#[embassy_executor::task] -async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { - let mut led = Output::new(led, Level::Low, OutputDrive::Standard); - - loop { - match receiver.recv().await { - LedState::On => led.set_high(), - LedState::Off => led.set_low(), - } - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let channel = CHANNEL.init(Channel::new()); - - unwrap!(spawner.spawn(send_task(channel.sender()))); - unwrap!(spawner.spawn(recv_task(p.P0_28.degrade(), channel.receiver()))); -} diff --git a/examples/nrf5340/src/bin/gpiote_port.rs b/examples/nrf5340/src/bin/gpiote_port.rs deleted file mode 100644 index 0cc911ad..00000000 --- a/examples/nrf5340/src/bin/gpiote_port.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::{info, unwrap}; -use embassy_executor::Spawner; -use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::task(pool_size = 4)] -async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { - loop { - pin.wait_for_low().await; - info!("Button {:?} pressed!", n); - pin.wait_for_high().await; - info!("Button {:?} released!", n); - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - info!("Starting!"); - - let btn1 = Input::new(p.P0_23.degrade(), Pull::Up); - let btn2 = Input::new(p.P0_24.degrade(), Pull::Up); - let btn3 = Input::new(p.P0_08.degrade(), Pull::Up); - let btn4 = Input::new(p.P0_09.degrade(), Pull::Up); - - unwrap!(spawner.spawn(button_task(1, btn1))); - unwrap!(spawner.spawn(button_task(2, btn2))); - unwrap!(spawner.spawn(button_task(3, btn3))); - unwrap!(spawner.spawn(button_task(4, btn4))); -} diff --git a/examples/nrf5340/src/bin/uart_idle.rs b/examples/nrf5340/src/bin/uart_idle.rs deleted file mode 100644 index 327fc4b2..00000000 --- a/examples/nrf5340/src/bin/uart_idle.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut config = uarte::Config::default(); - config.parity = uarte::Parity::EXCLUDED; - config.baudrate = uarte::Baudrate::BAUD115200; - - let irq = interrupt::take!(SERIAL0); - let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); - let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); - - info!("uarte initialized!"); - - // Message must be in SRAM - let mut buf = [0; 8]; - buf.copy_from_slice(b"Hello!\r\n"); - - unwrap!(tx.write(&buf).await); - info!("wrote hello in uart!"); - - loop { - info!("reading..."); - let n = unwrap!(rx.read_until_idle(&mut buf).await); - info!("got {} bytes", n); - } -} diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs deleted file mode 100644 index 0bbbfeaa..00000000 --- a/examples/nrf5340/src/bin/uart_split.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_nrf::peripherals::UARTETWISPI0; -use embassy_nrf::uarte::UarteRx; -use embassy_nrf::{interrupt, uarte}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use {defmt_rtt as _, panic_probe as _}; - -static CHANNEL: Channel = Channel::new(); - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut config = uarte::Config::default(); - config.parity = uarte::Parity::EXCLUDED; - config.baudrate = uarte::Baudrate::BAUD115200; - - let irq = interrupt::take!(SERIAL0); - let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); - let (mut tx, rx) = uart.split(); - - info!("uarte initialized!"); - - // Spawn a task responsible purely for reading - - unwrap!(spawner.spawn(reader(rx))); - - // Message must be in SRAM - { - let mut buf = [0; 23]; - buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); - - unwrap!(tx.write(&buf).await); - info!("wrote hello in uart!"); - } - - // Continue reading in this main task and write - // back out the buffer we receive from the read - // task. - loop { - let buf = CHANNEL.recv().await; - info!("writing..."); - unwrap!(tx.write(&buf).await); - } -} - -#[embassy_executor::task] -async fn reader(mut rx: UarteRx<'static, UARTETWISPI0>) { - let mut buf = [0; 8]; - loop { - info!("reading..."); - unwrap!(rx.read(&mut buf).await); - CHANNEL.send(buf).await; - } -} From 96b97c4711af0851f7d3a6785be13b0202b6e902 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Jan 2023 17:43:12 +0100 Subject: [PATCH 276/374] Update vscode settings for latest RA. --- .vscode/settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 402ed241..db37b64c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.formatOnSave": true, - "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.checkOnSave.noDefaultFeatures": true, + "rust-analyzer.check.allTargets": false, + "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, "rust-analyzer.cargo.target": "thumbv7em-none-eabi", From ce842fe28c27e6f57a05efc92bc417f6f7d7af64 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 11 Jan 2023 17:47:12 +0100 Subject: [PATCH 277/374] Refactor embassy-usb address handling to allow reordering of status resoponse --- embassy-nrf/src/usb.rs | 10 +++++----- embassy-rp/src/usb.rs | 15 ++++++++------- embassy-stm32/src/usb/usb.rs | 25 +++++++++++++------------ embassy-usb-driver/src/lib.rs | 9 ++++++--- embassy-usb/src/lib.rs | 23 ++++++++++++----------- 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index ed4d5cf3..6be4fec8 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -391,11 +391,6 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { .await } - #[inline] - fn set_address(&mut self, _addr: u8) { - // Nothing to do, the peripheral handles this. - } - fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { let regs = T::regs(); unsafe { @@ -841,6 +836,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let regs = T::regs(); regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); } + + async fn accept_set_address(&mut self, _addr: u8) { + self.accept().await; + // Nothing to do, the peripheral handles this. + } } fn dma_start() { diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 8361736a..2710c4ad 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -406,13 +406,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { .await } - #[inline] - fn set_address(&mut self, addr: u8) { - let regs = T::regs(); - trace!("setting addr: {}", addr); - unsafe { regs.addr_endp().write(|w| w.set_address(addr)) } - } - fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) { todo!(); } @@ -812,4 +805,12 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); } } + + async fn accept_set_address(&mut self, addr: u8) { + self.accept().await; + + let regs = T::regs(); + trace!("setting addr: {}", addr); + unsafe { regs.addr_endp().write(|w| w.set_address(addr)) } + } } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index cba4915c..062c7ef7 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -489,18 +489,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { .await } - #[inline] - fn set_address(&mut self, addr: u8) { - let regs = T::regs(); - trace!("setting addr: {}", addr); - unsafe { - regs.daddr().write(|w| { - w.set_ef(true); - w.set_add(addr); - }) - } - } - fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { // This can race, so do a retry loop. let reg = T::regs().epr(ep_addr.index() as _); @@ -1017,4 +1005,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }); } } + + async fn accept_set_address(&mut self, addr: u8) { + self.accept().await; + + let regs = T::regs(); + trace!("setting addr: {}", addr); + unsafe { + regs.daddr().write(|w| { + w.set_ef(true); + w.set_add(addr); + }) + } + } } diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index d7238dc7..64d64735 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -164,9 +164,6 @@ pub trait Bus { async fn poll(&mut self) -> Event; - /// Sets the device USB address to `addr`. - fn set_address(&mut self, addr: u8); - /// Enables or disables an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); @@ -306,6 +303,12 @@ pub trait ControlPipe { /// /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self); + + /// Accept SET_ADDRESS control and change bus address. + /// + /// For most drivers this function should firstly call `accept()` and then change the bus address. + /// However, there are peripherals (Synopsys USB OTG) that have reverse order. + async fn accept_set_address(&mut self, addr: u8); } pub trait EndpointIn: Endpoint { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 661b8411..096e8b07 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -122,10 +122,9 @@ struct Inner<'d, D: Driver<'d>> { /// Our device address, or 0 if none. address: u8, - /// When receiving a set addr control request, we have to apply it AFTER we've - /// finished handling the control request, as the status stage still has to be - /// handled with addr 0. - /// If true, do a set_addr after finishing the current control req. + /// SET_ADDRESS requests have special handling depending on the driver. + /// This flag indicates that requests must be handled by `ControlPipe::accept_set_address()` + /// instead of regular `accept()`. set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, @@ -254,11 +253,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { Direction::In => self.handle_control_in(req).await, Direction::Out => self.handle_control_out(req).await, } - - if self.inner.set_address_pending { - self.inner.bus.set_address(self.inner.address); - self.inner.set_address_pending = false; - } } async fn handle_control_in(&mut self, req: Request) { @@ -328,7 +322,14 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { trace!(" control out data: {:02x?}", data); match self.inner.handle_control_out(req, data) { - OutResponse::Accepted => self.control.accept().await, + OutResponse::Accepted => { + if self.inner.set_address_pending { + self.control.accept_set_address(self.inner.address).await; + self.inner.set_address_pending = false; + } else { + self.control.accept().await + } + } OutResponse::Rejected => self.control.reject().await, } } @@ -655,7 +656,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { buf[1] = descriptor_type::STRING; let mut pos = 2; for c in s.encode_utf16() { - if pos >= buf.len() { + if pos + 2 >= buf.len() { panic!("control buffer too small"); } From 065a0a1ee71c3e4333f2e38140e9ca86b61b59d2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Jan 2023 17:51:30 +0100 Subject: [PATCH 278/374] Update stm32-data. --- embassy-stm32/build.rs | 30 ++--- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/usb/usb.rs | 1 + embassy-stm32/src/usb_otg.rs | 213 ------------------------------- embassy-stm32/src/usb_otg/mod.rs | 138 ++++++++++++++++++++ stm32-data | 2 +- 6 files changed, 155 insertions(+), 231 deletions(-) delete mode 100644 embassy-stm32/src/usb_otg.rs create mode 100644 embassy-stm32/src/usb_otg/mod.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ddfa97b2..dbfc1370 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -277,22 +277,20 @@ fn main() { (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), (("usb", "DP"), quote!(crate::usb::DpPin)), (("usb", "DM"), quote!(crate::usb::DmPin)), - (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)), - (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)), - (("otghs", "DP"), quote!(crate::usb_otg::DpPin)), - (("otghs", "DM"), quote!(crate::usb_otg::DmPin)), - (("otghs", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)), - (("otghs", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)), - (("otghs", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)), - (("otghs", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)), - (("otghs", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)), - (("otghs", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)), - (("otghs", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)), - (("otghs", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)), - (("otghs", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)), - (("otghs", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)), - (("otghs", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)), - (("otghs", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)), + (("otg", "DP"), quote!(crate::usb_otg::DpPin)), + (("otg", "DM"), quote!(crate::usb_otg::DmPin)), + (("otg", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)), + (("otg", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)), + (("otg", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)), + (("otg", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)), + (("otg", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)), + (("otg", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)), + (("otg", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)), + (("otg", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)), + (("otg", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)), + (("otg", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)), + (("otg", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)), + (("otg", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)), (("can", "TX"), quote!(crate::can::TxPin)), (("can", "RX"), quote!(crate::can::RxPin)), (("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 16c46ca2..610c2488 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -58,7 +58,7 @@ pub mod spi; pub mod usart; #[cfg(all(usb, feature = "time"))] pub mod usb; -#[cfg(any(otgfs, otghs))] +#[cfg(otg)] pub mod usb_otg; #[cfg(iwdg)] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 062c7ef7..03e792a2 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -154,6 +154,7 @@ impl<'d, T: Instance> Driver<'d, T> { block_for(Duration::from_millis(100)); + #[cfg(not(usb_v4))] regs.btable().write(|w| w.set_btable(0)); dp.set_as_af(dp.af_num(), AFType::OutputPushPull); diff --git a/embassy-stm32/src/usb_otg.rs b/embassy-stm32/src/usb_otg.rs deleted file mode 100644 index f7faf12a..00000000 --- a/embassy-stm32/src/usb_otg.rs +++ /dev/null @@ -1,213 +0,0 @@ -use core::marker::PhantomData; - -use embassy_hal_common::into_ref; - -use crate::gpio::sealed::AFType; -use crate::rcc::RccPeripheral; -use crate::{peripherals, Peripheral}; - -macro_rules! config_ulpi_pins { - ($($pin:ident),*) => { - into_ref!($($pin),*); - // NOTE(unsafe) Exclusive access to the registers - critical_section::with(|_| unsafe { - $( - $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); - #[cfg(gpio_v2)] - $pin.set_speed(crate::gpio::Speed::VeryHigh); - )* - }) - }; -} - -/// USB PHY type -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum PhyType { - /// Internal Full-Speed PHY - /// - /// Available on most High-Speed peripherals. - InternalFullSpeed, - /// Internal High-Speed PHY - /// - /// Available on a few STM32 chips. - InternalHighSpeed, - /// External ULPI High-Speed PHY - ExternalHighSpeed, -} - -pub struct UsbOtg<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - _phy_type: PhyType, -} - -impl<'d, T: Instance> UsbOtg<'d, T> { - /// Initializes USB OTG peripheral with internal Full-Speed PHY - pub fn new_fs( - _peri: impl Peripheral

+ 'd, - dp: impl Peripheral

> + 'd, - dm: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(dp, dm); - - unsafe { - dp.set_as_af(dp.af_num(), AFType::OutputPushPull); - dm.set_as_af(dm.af_num(), AFType::OutputPushPull); - } - - Self { - phantom: PhantomData, - _phy_type: PhyType::InternalFullSpeed, - } - } - - /// Initializes USB OTG peripheral with external High-Speed PHY - pub fn new_hs_ulpi( - _peri: impl Peripheral

+ 'd, - ulpi_clk: impl Peripheral

> + 'd, - ulpi_dir: impl Peripheral

> + 'd, - ulpi_nxt: impl Peripheral

> + 'd, - ulpi_stp: impl Peripheral

> + 'd, - ulpi_d0: impl Peripheral

> + 'd, - ulpi_d1: impl Peripheral

> + 'd, - ulpi_d2: impl Peripheral

> + 'd, - ulpi_d3: impl Peripheral

> + 'd, - ulpi_d4: impl Peripheral

> + 'd, - ulpi_d5: impl Peripheral

> + 'd, - ulpi_d6: impl Peripheral

> + 'd, - ulpi_d7: impl Peripheral

> + 'd, - ) -> Self { - config_ulpi_pins!( - ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, - ulpi_d7 - ); - - Self { - phantom: PhantomData, - _phy_type: PhyType::ExternalHighSpeed, - } - } -} - -impl<'d, T: Instance> Drop for UsbOtg<'d, T> { - fn drop(&mut self) { - T::reset(); - T::disable(); - } -} - -pub(crate) mod sealed { - pub trait Instance { - const REGISTERS: *const (); - const HIGH_SPEED: bool; - const FIFO_DEPTH_WORDS: usize; - const ENDPOINT_COUNT: usize; - } -} - -pub trait Instance: sealed::Instance + RccPeripheral {} - -// Internal PHY pins -pin_trait!(DpPin, Instance); -pin_trait!(DmPin, Instance); - -// External PHY pins -pin_trait!(UlpiClkPin, Instance); -pin_trait!(UlpiDirPin, Instance); -pin_trait!(UlpiNxtPin, Instance); -pin_trait!(UlpiStpPin, Instance); -pin_trait!(UlpiD0Pin, Instance); -pin_trait!(UlpiD1Pin, Instance); -pin_trait!(UlpiD2Pin, Instance); -pin_trait!(UlpiD3Pin, Instance); -pin_trait!(UlpiD4Pin, Instance); -pin_trait!(UlpiD5Pin, Instance); -pin_trait!(UlpiD6Pin, Instance); -pin_trait!(UlpiD7Pin, Instance); - -foreach_peripheral!( - (otgfs, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGISTERS: *const () = crate::pac::$inst.0 as *const (); - const HIGH_SPEED: bool = false; - - cfg_if::cfg_if! { - if #[cfg(stm32f1)] { - const FIFO_DEPTH_WORDS: usize = 128; - const ENDPOINT_COUNT: usize = 8; - } else if #[cfg(any( - stm32f2, - stm32f401, - stm32f405, - stm32f407, - stm32f411, - stm32f415, - stm32f417, - stm32f427, - stm32f429, - stm32f437, - stm32f439, - ))] { - const FIFO_DEPTH_WORDS: usize = 320; - const ENDPOINT_COUNT: usize = 4; - } else if #[cfg(any( - stm32f412, - stm32f413, - stm32f423, - stm32f446, - stm32f469, - stm32f479, - stm32f7, - stm32l4, - stm32u5, - ))] { - const FIFO_DEPTH_WORDS: usize = 320; - const ENDPOINT_COUNT: usize = 6; - } else if #[cfg(stm32g0x1)] { - const FIFO_DEPTH_WORDS: usize = 512; - const ENDPOINT_COUNT: usize = 8; - } else { - compile_error!("USB_OTG_FS peripheral is not supported by this chip."); - } - } - } - - impl Instance for peripherals::$inst {} - }; - - (otghs, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGISTERS: *const () = crate::pac::$inst.0 as *const (); - const HIGH_SPEED: bool = true; - - cfg_if::cfg_if! { - if #[cfg(any( - stm32f2, - stm32f405, - stm32f407, - stm32f415, - stm32f417, - stm32f427, - stm32f429, - stm32f437, - stm32f439, - ))] { - const FIFO_DEPTH_WORDS: usize = 1024; - const ENDPOINT_COUNT: usize = 6; - } else if #[cfg(any( - stm32f446, - stm32f469, - stm32f479, - stm32f7, - stm32h7, - ))] { - const FIFO_DEPTH_WORDS: usize = 1024; - const ENDPOINT_COUNT: usize = 9; - } else { - compile_error!("USB_OTG_HS peripheral is not supported by this chip."); - } - } - } - - impl Instance for peripherals::$inst {} - }; -); diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs new file mode 100644 index 00000000..2ee2891d --- /dev/null +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -0,0 +1,138 @@ +use embassy_cortex_m::interrupt::Interrupt; + +use crate::peripherals; +use crate::rcc::RccPeripheral; + +pub(crate) mod sealed { + pub trait Instance { + const HIGH_SPEED: bool; + const FIFO_DEPTH_WORDS: u16; + const ENDPOINT_COUNT: usize; + + fn regs() -> crate::pac::otg::Otg; + } +} + +pub trait Instance: sealed::Instance + RccPeripheral { + type Interrupt: Interrupt; +} + +// Internal PHY pins +pin_trait!(DpPin, Instance); +pin_trait!(DmPin, Instance); + +// External PHY pins +pin_trait!(UlpiClkPin, Instance); +pin_trait!(UlpiDirPin, Instance); +pin_trait!(UlpiNxtPin, Instance); +pin_trait!(UlpiStpPin, Instance); +pin_trait!(UlpiD0Pin, Instance); +pin_trait!(UlpiD1Pin, Instance); +pin_trait!(UlpiD2Pin, Instance); +pin_trait!(UlpiD3Pin, Instance); +pin_trait!(UlpiD4Pin, Instance); +pin_trait!(UlpiD5Pin, Instance); +pin_trait!(UlpiD6Pin, Instance); +pin_trait!(UlpiD7Pin, Instance); + +foreach_interrupt!( + (USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => { + impl sealed::Instance for peripherals::USB_OTG_FS { + const HIGH_SPEED: bool = false; + + cfg_if::cfg_if! { + if #[cfg(stm32f1)] { + const FIFO_DEPTH_WORDS: u16 = 128; + const ENDPOINT_COUNT: usize = 8; + } else if #[cfg(any( + stm32f2, + stm32f401, + stm32f405, + stm32f407, + stm32f411, + stm32f415, + stm32f417, + stm32f427, + stm32f429, + stm32f437, + stm32f439, + ))] { + const FIFO_DEPTH_WORDS: u16 = 320; + const ENDPOINT_COUNT: usize = 4; + } else if #[cfg(any( + stm32f412, + stm32f413, + stm32f423, + stm32f446, + stm32f469, + stm32f479, + stm32f7, + stm32l4, + stm32u5, + ))] { + const FIFO_DEPTH_WORDS: u16 = 320; + const ENDPOINT_COUNT: usize = 6; + } else if #[cfg(stm32g0x1)] { + const FIFO_DEPTH_WORDS: u16 = 512; + const ENDPOINT_COUNT: usize = 8; + } else if #[cfg(stm32h7)] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 9; + } else { + compile_error!("USB_OTG_FS peripheral is not supported by this chip."); + } + } + + fn regs() -> crate::pac::otg::Otg { + crate::pac::USB_OTG_FS + } + } + + impl Instance for peripherals::USB_OTG_FS { + type Interrupt = crate::interrupt::$irq; + } + }; + + (USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => { + impl sealed::Instance for peripherals::USB_OTG_HS { + const HIGH_SPEED: bool = true; + + cfg_if::cfg_if! { + if #[cfg(any( + stm32f2, + stm32f405, + stm32f407, + stm32f415, + stm32f417, + stm32f427, + stm32f429, + stm32f437, + stm32f439, + ))] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 6; + } else if #[cfg(any( + stm32f446, + stm32f469, + stm32f479, + stm32f7, + stm32h7, + ))] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 9; + } else { + compile_error!("USB_OTG_HS peripheral is not supported by this chip."); + } + } + + fn regs() -> crate::pac::otg::Otg { + // OTG HS registers are a superset of FS registers + crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0) + } + } + + impl Instance for peripherals::USB_OTG_HS { + type Interrupt = crate::interrupt::$irq; + } + }; +); diff --git a/stm32-data b/stm32-data index 14a448c3..844793fc 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 14a448c318192fe9da1c95a4de1beb4ec4892f1c +Subproject commit 844793fc3da2ba3f12ab6a69b78cd8e6fb5497b4 From 0feecd5cded86d29870330341d5c2c8eb40688d0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 11 Jan 2023 17:56:47 +0100 Subject: [PATCH 279/374] stm32: add USB OTG support. Co-authored-by: Dario Nieuwenhuis --- embassy-stm32/src/usb_otg/mod.rs | 23 + embassy-stm32/src/usb_otg/usb.rs | 1346 ++++++++++++++++++++++++++++++ 2 files changed, 1369 insertions(+) create mode 100644 embassy-stm32/src/usb_otg/usb.rs diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 2ee2891d..84fef78c 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -3,6 +3,15 @@ use embassy_cortex_m::interrupt::Interrupt; use crate::peripherals; use crate::rcc::RccPeripheral; +#[cfg(feature = "nightly")] +mod usb; +#[cfg(feature = "nightly")] +pub use usb::*; + +// Using Instance::ENDPOINT_COUNT requires feature(const_generic_expr) so just define maximum eps +#[cfg(feature = "nightly")] +const MAX_EP_COUNT: usize = 9; + pub(crate) mod sealed { pub trait Instance { const HIGH_SPEED: bool; @@ -10,6 +19,8 @@ pub(crate) mod sealed { const ENDPOINT_COUNT: usize; fn regs() -> crate::pac::otg::Otg; + #[cfg(feature = "nightly")] + fn state() -> &'static super::State<{ super::MAX_EP_COUNT }>; } } @@ -86,6 +97,12 @@ foreach_interrupt!( fn regs() -> crate::pac::otg::Otg { crate::pac::USB_OTG_FS } + + #[cfg(feature = "nightly")] + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } } impl Instance for peripherals::USB_OTG_FS { @@ -129,6 +146,12 @@ foreach_interrupt!( // OTG HS registers are a superset of FS registers crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0) } + + #[cfg(feature = "nightly")] + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } } impl Instance for peripherals::USB_OTG_HS { diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs new file mode 100644 index 00000000..2d9b613e --- /dev/null +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -0,0 +1,1346 @@ +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::task::Poll; + +use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use embassy_usb_driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, + EndpointType, Event, Unsupported, +}; +use futures::future::poll_fn; + +use super::*; +use crate::gpio::sealed::AFType; +use crate::pac::otg::{regs, vals}; +use crate::rcc::sealed::RccPeripheral; +use crate::time::Hertz; + +macro_rules! config_ulpi_pins { + ($($pin:ident),*) => { + into_ref!($($pin),*); + // NOTE(unsafe) Exclusive access to the registers + critical_section::with(|_| unsafe { + $( + $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + $pin.set_speed(crate::gpio::Speed::VeryHigh); + )* + }) + }; +} + +// From `synopsys-usb-otg` crate: +// This calculation doesn't correspond to one in a Reference Manual. +// In fact, the required number of words is higher than indicated in RM. +// The following numbers are pessimistic and were figured out empirically. +const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30; + +/// USB PHY type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PhyType { + /// Internal Full-Speed PHY + /// + /// Available on most High-Speed peripherals. + InternalFullSpeed, + /// Internal High-Speed PHY + /// + /// Available on a few STM32 chips. + InternalHighSpeed, + /// External ULPI High-Speed PHY + ExternalHighSpeed, +} + +impl PhyType { + pub fn internal(&self) -> bool { + match self { + PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true, + PhyType::ExternalHighSpeed => false, + } + } + + pub fn high_speed(&self) -> bool { + match self { + PhyType::InternalFullSpeed => false, + PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true, + } + } + + pub fn to_dspd(&self) -> vals::Dspd { + match self { + PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL, + PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED, + PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED, + } + } +} + +/// Indicates that [State::ep_out_buffers] is empty. +const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX; + +pub struct State { + /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true. + ep0_setup_data: UnsafeCell<[u8; 8]>, + ep0_setup_ready: AtomicBool, + ep_in_wakers: [AtomicWaker; EP_COUNT], + ep_out_wakers: [AtomicWaker; EP_COUNT], + /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint. + /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY]. + ep_out_buffers: [UnsafeCell<*mut u8>; EP_COUNT], + ep_out_size: [AtomicU16; EP_COUNT], + bus_waker: AtomicWaker, +} + +unsafe impl Send for State {} +unsafe impl Sync for State {} + +impl State { + pub const fn new() -> Self { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); + const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY); + + Self { + ep0_setup_data: UnsafeCell::new([0u8; 8]), + ep0_setup_ready: AtomicBool::new(false), + ep_in_wakers: [NEW_AW; EP_COUNT], + ep_out_wakers: [NEW_AW; EP_COUNT], + ep_out_buffers: [NEW_BUF; EP_COUNT], + ep_out_size: [NEW_SIZE; EP_COUNT], + bus_waker: NEW_AW, + } + } +} + +#[derive(Debug, Clone, Copy)] +struct EndpointData { + ep_type: EndpointType, + max_packet_size: u16, + fifo_size_words: u16, +} + +pub struct Driver<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + irq: PeripheralRef<'d, T::Interrupt>, + ep_in: [Option; MAX_EP_COUNT], + ep_out: [Option; MAX_EP_COUNT], + ep_out_buffer: &'d mut [u8], + ep_out_buffer_offset: usize, + phy_type: PhyType, +} + +impl<'d, T: Instance> Driver<'d, T> { + /// Initializes USB OTG peripheral with internal Full-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_fs( + _peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + dp: impl Peripheral

> + 'd, + dm: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + ) -> Self { + into_ref!(dp, dm, irq); + + unsafe { + dp.set_as_af(dp.af_num(), AFType::OutputPushPull); + dm.set_as_af(dm.af_num(), AFType::OutputPushPull); + } + + Self { + phantom: PhantomData, + irq, + ep_in: [None; MAX_EP_COUNT], + ep_out: [None; MAX_EP_COUNT], + ep_out_buffer, + ep_out_buffer_offset: 0, + phy_type: PhyType::InternalFullSpeed, + } + } + + /// Initializes USB OTG peripheral with external High-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_hs_ulpi( + _peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + ulpi_clk: impl Peripheral

> + 'd, + ulpi_dir: impl Peripheral

> + 'd, + ulpi_nxt: impl Peripheral

> + 'd, + ulpi_stp: impl Peripheral

> + 'd, + ulpi_d0: impl Peripheral

> + 'd, + ulpi_d1: impl Peripheral

> + 'd, + ulpi_d2: impl Peripheral

> + 'd, + ulpi_d3: impl Peripheral

> + 'd, + ulpi_d4: impl Peripheral

> + 'd, + ulpi_d5: impl Peripheral

> + 'd, + ulpi_d6: impl Peripheral

> + 'd, + ulpi_d7: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + ) -> Self { + assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); + + config_ulpi_pins!( + ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, + ulpi_d7 + ); + + into_ref!(irq); + + Self { + phantom: PhantomData, + irq, + ep_in: [None; MAX_EP_COUNT], + ep_out: [None; MAX_EP_COUNT], + ep_out_buffer, + ep_out_buffer_offset: 0, + phy_type: PhyType::ExternalHighSpeed, + } + } + + // Returns total amount of words (u32) allocated in dedicated FIFO + fn allocated_fifo_words(&self) -> u16 { + RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in) + } + + fn alloc_endpoint( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result, EndpointAllocError> { + trace!( + "allocating type={:?} mps={:?} interval={}, dir={:?}", + ep_type, + max_packet_size, + interval, + D::dir() + ); + + if D::dir() == Direction::Out { + if self.ep_out_buffer_offset + max_packet_size as usize >= self.ep_out_buffer.len() { + error!("Not enough endpoint out buffer capacity"); + return Err(EndpointAllocError); + } + }; + + let fifo_size_words = match D::dir() { + Direction::Out => (max_packet_size + 3) / 4, + // INEPTXFD requires minimum size of 16 words + Direction::In => u16::max((max_packet_size + 3) / 4, 16), + }; + + if fifo_size_words + self.allocated_fifo_words() > T::FIFO_DEPTH_WORDS { + error!("Not enough FIFO capacity"); + return Err(EndpointAllocError); + } + + let eps = match D::dir() { + Direction::Out => &mut self.ep_out, + Direction::In => &mut self.ep_in, + }; + + // Find free endpoint slot + let slot = eps.iter_mut().enumerate().find(|(i, ep)| { + if *i == 0 && ep_type != EndpointType::Control { + // reserved for control pipe + false + } else { + ep.is_none() + } + }); + + let index = match slot { + Some((index, ep)) => { + *ep = Some(EndpointData { + ep_type, + max_packet_size, + fifo_size_words, + }); + index + } + None => { + error!("No free endpoints available"); + return Err(EndpointAllocError); + } + }; + + trace!(" index={}", index); + + if D::dir() == Direction::Out { + // Buffer capacity check was done above, now allocation cannot fail + unsafe { + *T::state().ep_out_buffers[index].get() = + self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _); + } + self.ep_out_buffer_offset += max_packet_size as usize; + } + + Ok(Endpoint { + _phantom: PhantomData, + info: EndpointInfo { + addr: EndpointAddress::from_parts(index, D::dir()), + ep_type, + max_packet_size, + interval, + }, + }) + } +} + +impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { + type EndpointOut = Endpoint<'d, T, Out>; + type EndpointIn = Endpoint<'d, T, In>; + type ControlPipe = ControlPipe<'d, T>; + type Bus = Bus<'d, T>; + + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval) + } + + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval) + } + + fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + let ep_out = self + .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + let ep_in = self + .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + assert_eq!(ep_out.info.addr.index(), 0); + assert_eq!(ep_in.info.addr.index(), 0); + + trace!("start"); + + ( + Bus { + phantom: PhantomData, + irq: self.irq, + ep_in: self.ep_in, + ep_out: self.ep_out, + phy_type: self.phy_type, + enabled: false, + }, + ControlPipe { + _phantom: PhantomData, + max_packet_size: control_max_packet_size, + ep_out, + ep_in, + }, + ) + } +} + +pub struct Bus<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + irq: PeripheralRef<'d, T::Interrupt>, + ep_in: [Option; MAX_EP_COUNT], + ep_out: [Option; MAX_EP_COUNT], + phy_type: PhyType, + enabled: bool, +} + +impl<'d, T: Instance> Bus<'d, T> { + fn restore_irqs() { + // SAFETY: atomic write + unsafe { + T::regs().gintmsk().write(|w| { + w.set_usbrst(true); + w.set_enumdnem(true); + w.set_usbsuspm(true); + w.set_wuim(true); + w.set_iepint(true); + w.set_oepint(true); + w.set_rxflvlm(true); + }); + } + } +} + +impl<'d, T: Instance> Bus<'d, T> { + fn init_fifo(&mut self) { + trace!("init_fifo"); + + let r = T::regs(); + + // Configure RX fifo size. All endpoints share the same FIFO area. + let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); + trace!("configuring rx fifo size={}", rx_fifo_size_words); + + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) }; + + // Configure TX (USB in direction) fifo size for each endpoint + let mut fifo_top = rx_fifo_size_words; + for i in 0..T::ENDPOINT_COUNT { + if let Some(ep) = self.ep_in[i] { + trace!( + "configuring tx fifo ep={}, offset={}, size={}", + i, + fifo_top, + ep.fifo_size_words + ); + + let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; + + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); + } + + fifo_top += ep.fifo_size_words; + } + } + + assert!( + fifo_top <= T::FIFO_DEPTH_WORDS, + "FIFO allocations exceeded maximum capacity" + ); + } + + fn configure_endpoints(&mut self) { + trace!("configure_endpoints"); + + let r = T::regs(); + + // Configure IN endpoints + for (index, ep) in self.ep_in.iter().enumerate() { + if let Some(ep) = ep { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepctl(index).write(|w| { + if index == 0 { + w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); + } else { + w.set_mpsiz(ep.max_packet_size); + w.set_eptyp(to_eptyp(ep.ep_type)); + w.set_sd0pid_sevnfrm(true); + } + }); + }); + } + } + + // Configure OUT endpoints + for (index, ep) in self.ep_out.iter().enumerate() { + if let Some(ep) = ep { + // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.doepctl(index).write(|w| { + if index == 0 { + w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); + } else { + w.set_mpsiz(ep.max_packet_size); + w.set_eptyp(to_eptyp(ep.ep_type)); + w.set_sd0pid_sevnfrm(true); + } + }); + + r.doeptsiz(index).modify(|w| { + w.set_xfrsiz(ep.max_packet_size as _); + if index == 0 { + w.set_rxdpid_stupcnt(1); + } else { + w.set_pktcnt(1); + } + }); + }); + } + } + + // Enable IRQs for allocated endpoints + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { + r.daintmsk().modify(|w| { + w.set_iepm(ep_irq_mask(&self.ep_in)); + // OUT interrupts not used, handled in RXFLVL + // w.set_oepm(ep_irq_mask(&self.ep_out)); + }); + } + } + + fn disable(&mut self) { + self.irq.disable(); + self.irq.remove_handler(); + + ::disable(); + + #[cfg(stm32l4)] + unsafe { + crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); + // Cannot disable PWR, because other peripherals might be using it + } + } + + fn on_interrupt(_: *mut ()) { + trace!("irq"); + let r = T::regs(); + let state = T::state(); + + // SAFETY: atomic read/write + let ints = unsafe { r.gintsts().read() }; + if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() { + // Mask interrupts and notify `Bus` to process them + unsafe { r.gintmsk().write(|_| {}) }; + T::state().bus_waker.wake(); + } + + // Handle RX + // SAFETY: atomic read with no side effects + while unsafe { r.gintsts().read().rxflvl() } { + // SAFETY: atomic "pop" register + let status = unsafe { r.grxstsp().read() }; + let ep_num = status.epnum() as usize; + let len = status.bcnt() as usize; + + assert!(ep_num < T::ENDPOINT_COUNT); + + match status.pktstsd() { + vals::Pktstsd::SETUP_DATA_RX => { + trace!("SETUP_DATA_RX"); + assert!(len == 8, "invalid SETUP packet length={}", len); + assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num); + + if state.ep0_setup_ready.load(Ordering::Relaxed) == false { + // SAFETY: exclusive access ensured by atomic bool + let data = unsafe { &mut *state.ep0_setup_data.get() }; + // SAFETY: FIFO reads are exclusive to this IRQ + unsafe { + data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); + } + state.ep0_setup_ready.store(true, Ordering::Release); + state.ep_out_wakers[0].wake(); + } else { + error!("received SETUP before previous finished processing"); + // discard FIFO + // SAFETY: FIFO reads are exclusive to IRQ + unsafe { + r.fifo(0).read(); + r.fifo(0).read(); + } + } + } + vals::Pktstsd::OUT_DATA_RX => { + trace!("OUT_DATA_RX ep={} len={}", ep_num, len); + + if state.ep_out_size[ep_num].load(Ordering::Acquire) == EP_OUT_BUFFER_EMPTY { + // SAFETY: Buffer size is allocated to be equal to endpoint's maximum packet size + // We trust the peripheral to not exceed its configured MPSIZ + let buf = unsafe { core::slice::from_raw_parts_mut(*state.ep_out_buffers[ep_num].get(), len) }; + + for chunk in buf.chunks_mut(4) { + // RX FIFO is shared so always read from fifo(0) + // SAFETY: FIFO reads are exclusive to IRQ + let data = unsafe { r.fifo(0).read().0 }; + chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]); + } + + state.ep_out_size[ep_num].store(len as u16, Ordering::Release); + state.ep_out_wakers[ep_num].wake(); + } else { + error!("ep_out buffer overflow index={}", ep_num); + + // discard FIFO data + let len_words = (len + 3) / 4; + for _ in 0..len_words { + // SAFETY: FIFO reads are exclusive to IRQ + unsafe { r.fifo(0).read().data() }; + } + } + } + vals::Pktstsd::OUT_DATA_DONE => { + trace!("OUT_DATA_DONE ep={}", ep_num); + } + vals::Pktstsd::SETUP_DATA_DONE => { + trace!("SETUP_DATA_DONE ep={}", ep_num); + } + x => trace!("unknown PKTSTS: {}", x.0), + } + } + + // IN endpoint interrupt + if ints.iepint() { + // SAFETY: atomic read with no side effects + let mut ep_mask = unsafe { r.daint().read().iepint() }; + let mut ep_num = 0; + + // Iterate over endpoints while there are non-zero bits in the mask + while ep_mask != 0 { + if ep_mask & 1 != 0 { + // SAFETY: atomic read with no side effects + let ep_ints = unsafe { r.diepint(ep_num).read() }; + + // clear all + // SAFETY: DIEPINT is exclusive to IRQ + unsafe { r.diepint(ep_num).write_value(ep_ints) }; + + // TXFE is cleared in DIEPEMPMSK + if ep_ints.txfe() { + // SAFETY: DIEPEMPMSK is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepempmsk().modify(|w| { + w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num)); + }); + }); + } + + state.ep_in_wakers[ep_num].wake(); + trace!("in ep={} irq val={:b}", ep_num, ep_ints.0); + } + + ep_mask >>= 1; + ep_num += 1; + } + } + + // not needed? reception handled in rxflvl + // OUT endpoint interrupt + // if ints.oepint() { + // let mut ep_mask = r.daint().read().oepint(); + // let mut ep_num = 0; + + // while ep_mask != 0 { + // if ep_mask & 1 != 0 { + // let ep_ints = r.doepint(ep_num).read(); + // // clear all + // r.doepint(ep_num).write_value(ep_ints); + // state.ep_out_wakers[ep_num].wake(); + // trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0); + // } + + // ep_mask >>= 1; + // ep_num += 1; + // } + // } + } +} + +impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { + async fn poll(&mut self) -> Event { + poll_fn(move |cx| { + // TODO: implement VBUS detection + if !self.enabled { + return Poll::Ready(Event::PowerDetected); + } + + let r = T::regs(); + + T::state().bus_waker.register(cx.waker()); + + let ints = unsafe { r.gintsts().read() }; + if ints.usbrst() { + trace!("reset"); + + self.init_fifo(); + self.configure_endpoints(); + + // Reset address + // SAFETY: DCFG is shared with `ControlPipe` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.dcfg().modify(|w| { + w.set_dad(0); + }); + }); + + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_usbrst(true)) }; // clear + Self::restore_irqs(); + } + + if ints.enumdne() { + trace!("enumdne"); + + // SAFETY: atomic read with no side effects + let speed = unsafe { r.dsts().read().enumspd() }; + trace!(" speed={}", speed.0); + + // SAFETY: register is only accessed by `Bus` under `&mut self` + unsafe { + r.gusbcfg().modify(|w| { + w.set_trdt(calculate_trdt(speed, T::frequency())); + }) + }; + + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_enumdne(true)) }; // clear + Self::restore_irqs(); + + return Poll::Ready(Event::Reset); + } + + if ints.usbsusp() { + trace!("suspend"); + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_usbsusp(true)) }; // clear + Self::restore_irqs(); + return Poll::Ready(Event::Suspend); + } + + if ints.wkupint() { + trace!("resume"); + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_wkupint(true)) }; // clear + Self::restore_irqs(); + return Poll::Ready(Event::Resume); + } + + Poll::Pending + }) + .await + } + + fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { + trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled); + + assert!( + ep_addr.index() < T::ENDPOINT_COUNT, + "endpoint_set_stalled index {} out of range", + ep_addr.index() + ); + + let regs = T::regs(); + match ep_addr.direction() { + Direction::Out => { + // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + regs.doepctl(ep_addr.index()).modify(|w| { + w.set_stall(stalled); + }); + }); + + T::state().ep_out_wakers[ep_addr.index()].wake(); + } + Direction::In => { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + regs.diepctl(ep_addr.index()).modify(|w| { + w.set_stall(stalled); + }); + }); + + T::state().ep_in_wakers[ep_addr.index()].wake(); + } + } + } + + fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + assert!( + ep_addr.index() < T::ENDPOINT_COUNT, + "endpoint_is_stalled index {} out of range", + ep_addr.index() + ); + + let regs = T::regs(); + + // SAFETY: atomic read with no side effects + match ep_addr.direction() { + Direction::Out => unsafe { regs.doepctl(ep_addr.index()).read().stall() }, + Direction::In => unsafe { regs.diepctl(ep_addr.index()).read().stall() }, + } + } + + fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled); + + assert!( + ep_addr.index() < T::ENDPOINT_COUNT, + "endpoint_set_enabled index {} out of range", + ep_addr.index() + ); + + let r = T::regs(); + match ep_addr.direction() { + Direction::Out => { + // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + // cancel transfer if active + if !enabled && r.doepctl(ep_addr.index()).read().epena() { + r.doepctl(ep_addr.index()).modify(|w| { + w.set_snak(true); + w.set_epdis(true); + }) + } + + r.doepctl(ep_addr.index()).modify(|w| { + w.set_usbaep(enabled); + }) + }); + } + Direction::In => { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + // cancel transfer if active + if !enabled && r.diepctl(ep_addr.index()).read().epena() { + r.diepctl(ep_addr.index()).modify(|w| { + w.set_snak(true); + w.set_epdis(true); + }) + } + + r.diepctl(ep_addr.index()).modify(|w| { + w.set_usbaep(enabled); + }) + }); + } + } + } + + async fn enable(&mut self) { + trace!("enable"); + + // SAFETY: registers are only accessed by `Bus` under `&mut self` + unsafe { + #[cfg(stm32l4)] + { + crate::peripherals::PWR::enable(); + critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); + } + + #[cfg(stm32h7)] + { + // If true, VDD33USB is generated by internal regulator from VDD50USB + // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) + // TODO: unhardcode + let internal_regulator = false; + + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.cr3().modify(|w| { + w.set_usb33den(true); + w.set_usbregen(internal_regulator); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.cr3().read().usb33rdy() {} + + // Use internal 48MHz HSI clock. Should be enabled in RCC by default. + critical_section::with(|_| { + crate::pac::RCC + .d2ccip2r() + .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) + }); + + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpien(ulpien); + } else { + w.set_usb_otg_fs_ulpien(ulpien); + } + }); + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpilpen(ulpien); + } else { + w.set_usb_otg_fs_ulpilpen(ulpien); + } + }); + }); + } + + #[cfg(stm32u5)] + { + // Enable USB power + critical_section::with(|_| { + crate::pac::RCC.ahb3enr().modify(|w| { + w.set_pwren(true); + }); + cortex_m::asm::delay(2); + + crate::pac::PWR.svmcr().modify(|w| { + w.set_usv(true); + w.set_uvmen(true); + }); + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.svmsr().read().vddusbrdy() {} + + // Select HSI48 as USB clock source. + critical_section::with(|_| { + crate::pac::RCC.ccipr1().modify(|w| { + w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); + }) + }); + } + + ::enable(); + ::reset(); + + self.irq.set_handler(Self::on_interrupt); + self.irq.unpend(); + self.irq.enable(); + + let r = T::regs(); + let core_id = r.cid().read().0; + info!("Core id {:08x}", core_id); + + // Wait for AHB ready. + while !r.grstctl().read().ahbidl() {} + + // Configure as device. + r.gusbcfg().write(|w| { + // Force device mode + w.set_fdmod(true); + // Enable internal full-speed PHY + w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); + }); + + // Configuring Vbus sense and SOF output + match core_id { + 0x0000_1200 | 0x0000_1100 => { + assert!(self.phy_type != PhyType::InternalHighSpeed); + + r.gccfg_v1().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal()); + }); + + // F429-like chips have the GCCFG.NOVBUSSENS bit + r.gccfg_v1().modify(|w| { + w.set_novbussens(true); + w.set_vbusasen(false); + w.set_vbusbsen(false); + w.set_sofouten(false); + }); + } + 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { + // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning + r.gccfg_v2().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); + w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); + }); + + r.gccfg_v2().modify(|w| { + w.set_vbden(false); + }); + + // Force B-peripheral session + r.gotgctl().modify(|w| { + w.set_bvaloen(true); + w.set_bvaloval(true); + }); + } + _ => unimplemented!("Unknown USB core id {:X}", core_id), + } + + // Soft disconnect. + r.dctl().write(|w| w.set_sdis(true)); + + // Set speed. + r.dcfg().write(|w| { + w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); + w.set_dspd(self.phy_type.to_dspd()); + }); + + // Unmask transfer complete EP interrupt + r.diepmsk().write(|w| { + w.set_xfrcm(true); + }); + + // Unmask and clear core interrupts + Bus::::restore_irqs(); + r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); + + // Unmask global interrupt + r.gahbcfg().write(|w| { + w.set_gint(true); // unmask global interrupt + }); + + // Connect + r.dctl().write(|w| w.set_sdis(false)); + } + + self.enabled = true; + } + + async fn disable(&mut self) { + trace!("disable"); + + Bus::disable(self); + + self.enabled = false; + } + + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + Err(Unsupported) + } +} + +impl<'d, T: Instance> Drop for Bus<'d, T> { + fn drop(&mut self) { + Bus::disable(self); + } +} + +trait Dir { + fn dir() -> Direction; +} + +pub enum In {} +impl Dir for In { + fn dir() -> Direction { + Direction::In + } +} + +pub enum Out {} +impl Dir for Out { + fn dir() -> Direction { + Direction::Out + } +} + +pub struct Endpoint<'d, T: Instance, D> { + _phantom: PhantomData<(&'d mut T, D)>, + info: EndpointInfo, +} + +impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> { + fn info(&self) -> &EndpointInfo { + &self.info + } + + async fn wait_enabled(&mut self) {} +} + +impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { + fn info(&self) -> &EndpointInfo { + &self.info + } + + async fn wait_enabled(&mut self) {} +} + +impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + trace!("read start len={}", buf.len()); + + poll_fn(|cx| { + let index = self.info.addr.index(); + let state = T::state(); + + state.ep_out_wakers[index].register(cx.waker()); + + let len = state.ep_out_size[index].load(Ordering::Relaxed); + if len != EP_OUT_BUFFER_EMPTY { + trace!("read done len={}", len); + + if len as usize > buf.len() { + return Poll::Ready(Err(EndpointError::BufferOverflow)); + } + + // SAFETY: exclusive access ensured by `ep_out_size` atomic variable + let data = unsafe { core::slice::from_raw_parts(*state.ep_out_buffers[index].get(), len as usize) }; + buf[..len as usize].copy_from_slice(data); + + // Release buffer + state.ep_out_size[index].store(EP_OUT_BUFFER_EMPTY, Ordering::Release); + + // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Bus` so a critical section is needed for RMW + critical_section::with(|_| unsafe { + // Receive 1 packet + T::regs().doeptsiz(index).modify(|w| { + w.set_xfrsiz(self.info.max_packet_size as _); + w.set_pktcnt(1); + }); + + // Clear NAK to indicate we are ready to receive more data + T::regs().doepctl(index).modify(|w| { + w.set_cnak(true); + }); + }); + + Poll::Ready(Ok(len as usize)) + } else { + Poll::Pending + } + }) + .await + } +} + +impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if buf.len() > self.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + let r = T::regs(); + let index = self.info.addr.index(); + let state = T::state(); + + // Wait for previous transfer to complete + poll_fn(|cx| { + state.ep_in_wakers[index].register(cx.waker()); + + // SAFETY: atomic read with no side effects + if unsafe { r.diepctl(index).read().epena() } { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + + if buf.len() > 0 { + poll_fn(|cx| { + state.ep_in_wakers[index].register(cx.waker()); + + let size_words = (buf.len() + 3) / 4; + + // SAFETY: atomic read with no side effects + let fifo_space = unsafe { r.dtxfsts(index).read().ineptfsav() as usize }; + if size_words > fifo_space { + // Not enough space in fifo, enable tx fifo empty interrupt + // SAFETY: DIEPEMPMSK is shared with IRQ so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepempmsk().modify(|w| { + w.set_ineptxfem(w.ineptxfem() | (1 << index)); + }); + }); + + trace!("tx fifo for ep={} full, waiting for txfe", index); + + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await + } + + // SAFETY: DIEPTSIZ is exclusive to this endpoint under `&mut self` + unsafe { + // Setup transfer size + r.dieptsiz(index).write(|w| { + w.set_mcnt(1); + w.set_pktcnt(1); + w.set_xfrsiz(buf.len() as _); + }); + } + + // SAFETY: DIEPCTL is shared with `Bus` so a critical section is needed for RMW + critical_section::with(|_| unsafe { + // Enable endpoint + r.diepctl(index).modify(|w| { + w.set_cnak(true); + w.set_epena(true); + }); + }); + + // Write data to FIFO + for chunk in buf.chunks(4) { + let mut tmp = [0u8; 4]; + tmp[0..chunk.len()].copy_from_slice(chunk); + // SAFETY: FIFO is exclusive to this endpoint under `&mut self` + unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; + } + + trace!("WRITE OK"); + + Ok(()) + } +} + +pub struct ControlPipe<'d, T: Instance> { + _phantom: PhantomData<&'d mut T>, + max_packet_size: u16, + ep_in: Endpoint<'d, T, In>, + ep_out: Endpoint<'d, T, Out>, +} + +impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { + fn max_packet_size(&self) -> usize { + usize::from(self.max_packet_size) + } + + async fn setup(&mut self) -> [u8; 8] { + poll_fn(|cx| { + let state = T::state(); + + state.ep_out_wakers[0].register(cx.waker()); + + if state.ep0_setup_ready.load(Ordering::Relaxed) { + let data = unsafe { *state.ep0_setup_data.get() }; + state.ep0_setup_ready.store(false, Ordering::Release); + + // EP0 should not be controlled by `Bus` so this RMW does not need a critical section + unsafe { + // Receive 1 SETUP packet + T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| { + w.set_rxdpid_stupcnt(1); + }); + + // Clear NAK to indicate we are ready to receive more data + T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { + w.set_cnak(true); + }) + }; + + trace!("SETUP received: {:?}", data); + Poll::Ready(data) + } else { + trace!("SETUP waiting"); + Poll::Pending + } + }) + .await + } + + async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { + trace!("control: data_out"); + let len = self.ep_out.read(buf).await?; + trace!("control: data_out read: {:?}", &buf[..len]); + Ok(len) + } + + async fn data_in(&mut self, data: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { + trace!("control: data_in write: {:?}", data); + self.ep_in.write(data).await?; + + // wait for status response from host after sending the last packet + if last { + trace!("control: data_in waiting for status"); + self.ep_out.read(&mut []).await?; + trace!("control: complete"); + } + + Ok(()) + } + + async fn accept(&mut self) { + trace!("control: accept"); + + self.ep_in.write(&[]).await.ok(); + + trace!("control: accept OK"); + } + + async fn reject(&mut self) { + trace!("control: reject"); + + // EP0 should not be controlled by `Bus` so this RMW does not need a critical section + unsafe { + let regs = T::regs(); + regs.diepctl(self.ep_in.info.addr.index()).modify(|w| { + w.set_stall(true); + }); + regs.doepctl(self.ep_out.info.addr.index()).modify(|w| { + w.set_stall(true); + }); + } + } + + async fn accept_set_address(&mut self, addr: u8) { + trace!("setting addr: {}", addr); + critical_section::with(|_| unsafe { + T::regs().dcfg().modify(|w| { + w.set_dad(addr); + }); + }); + + // synopsys driver requires accept to be sent after changing address + self.accept().await + } +} + +/// Translates HAL [EndpointType] into PAC [vals::Eptyp] +fn to_eptyp(ep_type: EndpointType) -> vals::Eptyp { + match ep_type { + EndpointType::Control => vals::Eptyp::CONTROL, + EndpointType::Isochronous => vals::Eptyp::ISOCHRONOUS, + EndpointType::Bulk => vals::Eptyp::BULK, + EndpointType::Interrupt => vals::Eptyp::INTERRUPT, + } +} + +/// Calculates total allocated FIFO size in words +fn ep_fifo_size(eps: &[Option]) -> u16 { + eps.iter().map(|ep| ep.map(|ep| ep.fifo_size_words).unwrap_or(0)).sum() +} + +/// Generates IRQ mask for enabled endpoints +fn ep_irq_mask(eps: &[Option]) -> u16 { + eps.iter().enumerate().fold( + 0, + |mask, (index, ep)| { + if ep.is_some() { + mask | (1 << index) + } else { + mask + } + }, + ) +} + +/// Calculates MPSIZ value for EP0, which uses special values. +fn ep0_mpsiz(max_packet_size: u16) -> u16 { + match max_packet_size { + 8 => 0b11, + 16 => 0b10, + 32 => 0b01, + 64 => 0b00, + other => panic!("Unsupported EP0 size: {}", other), + } +} + +fn calculate_trdt(speed: vals::Dspd, ahb_freq: Hertz) -> u8 { + match speed { + vals::Dspd::HIGH_SPEED => { + // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) + if ahb_freq.0 >= 30_000_000 { + 0x9 + } else { + panic!("AHB frequency is too low") + } + } + vals::Dspd::FULL_SPEED_EXTERNAL | vals::Dspd::FULL_SPEED_INTERNAL => { + // From RM0431 (F72xx), RM0090 (F429) + match ahb_freq.0 { + 0..=14_199_999 => panic!("AHB frequency is too low"), + 14_200_000..=14_999_999 => 0xF, + 15_000_000..=15_999_999 => 0xE, + 16_000_000..=17_199_999 => 0xD, + 17_200_000..=18_499_999 => 0xC, + 18_500_000..=19_999_999 => 0xB, + 20_000_000..=21_799_999 => 0xA, + 21_800_000..=23_999_999 => 0x9, + 24_000_000..=27_499_999 => 0x8, + 27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE + 32_000_000..=u32::MAX => 0x6, + } + } + _ => unimplemented!(), + } +} From 041531c82911053671e71b7554d1020021f45921 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Jan 2023 17:57:22 +0100 Subject: [PATCH 280/374] stm32/rcc: fix u5 pll, add hsi48. --- embassy-stm32/src/rcc/u5.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 960c4532..2ba339ec 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -295,6 +295,7 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub apb3_pre: APBPrescaler, + pub hsi48: bool, } impl Default for Config { @@ -305,6 +306,7 @@ impl Default for Config { apb1_pre: Default::default(), apb2_pre: Default::default(), apb3_pre: Default::default(), + hsi48: false, } } } @@ -320,7 +322,6 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().write(|w| { w.set_msipllen(false); w.set_msison(true); - w.set_msison(true); }); while !RCC.cr().read().msisrdy() {} @@ -340,9 +341,20 @@ pub(crate) unsafe fn init(config: Config) { } ClockSrc::PLL1R(src, m, n, div) => { let freq = match src { - PllSrc::MSI(_) => MSIRange::default().into(), - PllSrc::HSE(hertz) => hertz.0, - PllSrc::HSI16 => HSI_FREQ.0, + PllSrc::MSI(_) => { + // TODO: enable MSI + MSIRange::default().into() + } + PllSrc::HSE(hertz) => { + // TODO: enable HSE + hertz.0 + } + PllSrc::HSI16 => { + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + HSI_FREQ.0 + } }; // disable @@ -355,6 +367,7 @@ pub(crate) unsafe fn init(config: Config) { RCC.pll1cfgr().write(|w| { w.set_pllm(m.into()); w.set_pllsrc(src.into()); + w.set_pllren(true); }); RCC.pll1divr().modify(|w| { @@ -365,15 +378,16 @@ pub(crate) unsafe fn init(config: Config) { // Enable PLL RCC.cr().modify(|w| w.set_pllon(0, true)); while !RCC.cr().read().pllrdy(0) {} - RCC.pll1cfgr().modify(|w| w.set_pllren(true)); - - RCC.cr().write(|w| w.set_pllon(0, true)); - while !RCC.cr().read().pllrdy(0) {} pll_ck } }; + if config.hsi48 { + RCC.cr().modify(|w| w.set_hsi48on(true)); + while !RCC.cr().read().hsi48rdy() {} + } + // TODO make configurable let power_vos = VoltageScale::Range4; From 1af102a1aaa11d03bfa37831a3284546b605efd8 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 11 Jan 2023 17:58:15 +0100 Subject: [PATCH 281/374] stm32 otg: add examples. --- examples/stm32f4/Cargo.toml | 5 +- examples/stm32f4/src/bin/usb_serial.rs | 106 ++++++++++++++++++++++++ examples/stm32f7/Cargo.toml | 1 + examples/stm32f7/src/bin/usb_serial.rs | 107 ++++++++++++++++++++++++ examples/stm32h7/Cargo.toml | 1 + examples/stm32h7/src/bin/usb_serial.rs | 106 ++++++++++++++++++++++++ examples/stm32l4/Cargo.toml | 4 +- examples/stm32l4/src/bin/usb_serial.rs | 108 +++++++++++++++++++++++++ examples/stm32u5/Cargo.toml | 1 + examples/stm32u5/src/bin/usb_serial.rs | 108 +++++++++++++++++++++++++ 10 files changed, 542 insertions(+), 5 deletions(-) create mode 100644 examples/stm32f4/src/bin/usb_serial.rs create mode 100644 examples/stm32f7/src/bin/usb_serial.rs create mode 100644 examples/stm32h7/src/bin/usb_serial.rs create mode 100644 examples/stm32l4/src/bin/usb_serial.rs create mode 100644 examples/stm32u5/src/bin/usb_serial.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 62d3f08d..252d6085 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,6 +10,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", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -26,5 +27,5 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" -usb-device = "0.2" -usbd-serial = "0.1.1" +[profile.release] +debug = 2 diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs new file mode 100644 index 00000000..01464776 --- /dev/null +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::{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 futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(48)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index b80dbbf9..ea4cbd80 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -11,6 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs new file mode 100644 index 00000000..688bd0da --- /dev/null +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::{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 futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.hse = Some(mhz(8)); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(200)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d30c42b1..ff38440a 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -11,6 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs new file mode 100644 index 00000000..b319d12c --- /dev/null +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::{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 futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(400)); + config.rcc.hclk = Some(mhz(200)); + config.rcc.pll1.q_ck = Some(mhz(100)); + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 45d3dd36..5627760e 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -12,6 +12,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", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -26,6 +27,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" -usb-device = "0.2" -usbd-serial = "0.1.1" - diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs new file mode 100644 index 00000000..3e38b10a --- /dev/null +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -0,0 +1,108 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::rcc::*; +use embassy_stm32::usb_otg::{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 futures::future::join; +use panic_probe as _; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None); + config.rcc.hsi48 = true; + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.max_packet_size_0 = 64; + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index d88fdda5..2b02eda9 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -9,6 +9,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", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs new file mode 100644 index 00000000..c846836b --- /dev/null +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -0,0 +1,108 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::rcc::*; +use embassy_stm32::usb_otg::{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 futures::future::join; +use panic_probe as _; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided); + //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); + config.rcc.hsi48 = true; + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From b0529bc943c9da0eb5f43335d06779d6064b765a Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 6 Jan 2023 22:21:39 +1100 Subject: [PATCH 282/374] Support codesigning in the firmware updater This commit provides a method to verify that firmware has been signed with a private key given its public key. The implementation uses ed25519-dalek as the signature verifier. An "ed25519" feature is required to enable the functionality. When disabled (the default), calling the firmware updater's verify method will return a failure. --- .github/workflows/rust.yml | 10 +- embassy-boot/boot/Cargo.toml | 16 +- embassy-boot/boot/src/lib.rs | 367 ++++++++++++++++++++++- examples/boot/application/nrf/Cargo.toml | 5 + examples/boot/application/nrf/README.md | 2 +- 5 files changed, 382 insertions(+), 18 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b93c8783..3bfe5ef0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -68,5 +68,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Test - run: cd embassy-sync && cargo test + + - name: Test boot + working-directory: ./embassy-boot/boot + run: cargo test && cargo test --features "ed25519-dalek" && cargo test --features "ed25519-salty" + + - name: Test sync + working-directory: ./embassy-sync + run: cargo test diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index ae4efbd2..0b0c77b1 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -25,12 +25,26 @@ features = ["defmt"] [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } +ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" embedded-storage-async = "0.3.0" +salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } +signature = { version = "1.6.4", default-features = false } [dev-dependencies] log = "0.4" env_logger = "0.9" -rand = "0.8" +rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } + +[dev-dependencies.ed25519-dalek] +default_features = false +features = ["rand", "std", "u32_backend"] + +[features] +ed25519-dalek = ["dep:ed25519-dalek", "_verify"] +ed25519-salty = ["dep:salty", "_verify"] + +#Internal features +_verify = [] \ No newline at end of file diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 76b14bc8..be254e9d 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -52,6 +52,16 @@ pub enum BootError { BadMagic, } +#[cfg(feature = "defmt")] +impl defmt::Format for BootError { + fn format(&self, fmt: defmt::Formatter) { + match self { + BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), + BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), + } + } +} + impl From for BootError where E: NorFlashError, @@ -557,6 +567,33 @@ where self.state } } +/// Errors returned by FirmwareUpdater +#[derive(Debug)] +pub enum FirmwareUpdaterError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Signature errors. + Signature(signature::Error), +} + +#[cfg(feature = "defmt")] +impl defmt::Format for FirmwareUpdaterError { + fn format(&self, fmt: defmt::Formatter) { + match self { + FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), + FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), + } + } +} + +impl From for FirmwareUpdaterError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + FirmwareUpdaterError::Flash(error.kind()) + } +} /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state @@ -609,7 +646,11 @@ impl FirmwareUpdater { /// 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 { + 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) { @@ -619,12 +660,126 @@ impl FirmwareUpdater { } } + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _flash).await + } + /// 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 async fn mark_updated(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic(aligned, SWAP_MAGIC, flash).await } @@ -634,7 +789,11 @@ impl FirmwareUpdater { /// # Safety /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + pub async fn mark_booted( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic(aligned, BOOT_MAGIC, flash).await } @@ -644,7 +803,7 @@ impl FirmwareUpdater { aligned: &mut [u8], magic: u8, flash: &mut F, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { flash.read(self.state.from as u32, aligned).await?; if aligned.iter().any(|&b| b != magic) { @@ -672,7 +831,7 @@ impl FirmwareUpdater { data: &[u8], flash: &mut F, block_size: usize, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); flash @@ -700,7 +859,10 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub async fn prepare_update(&mut self, flash: &mut F) -> Result { + 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); @@ -717,7 +879,11 @@ impl FirmwareUpdater { /// 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 { + 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) { @@ -727,12 +893,126 @@ impl FirmwareUpdater { } } + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub fn verify_and_mark_updated_blocking( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) + } + /// 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> { + #[cfg(not(feature = "_verify"))] + pub fn mark_updated_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic_blocking(aligned, SWAP_MAGIC, flash) } @@ -742,7 +1022,11 @@ impl FirmwareUpdater { /// # 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> { + pub fn mark_booted_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic_blocking(aligned, BOOT_MAGIC, flash) } @@ -752,7 +1036,7 @@ impl FirmwareUpdater { aligned: &mut [u8], magic: u8, flash: &mut F, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { flash.read(self.state.from as u32, aligned)?; if aligned.iter().any(|&b| b != magic) { @@ -780,7 +1064,7 @@ impl FirmwareUpdater { data: &[u8], flash: &mut F, block_size: usize, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); flash.erase( @@ -804,7 +1088,10 @@ impl FirmwareUpdater { /// /// 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 { + 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); @@ -953,6 +1240,7 @@ mod tests { } #[test] + #[cfg(not(feature = "_verify"))] fn test_swap_state() { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); @@ -1022,6 +1310,7 @@ mod tests { } #[test] + #[cfg(not(feature = "_verify"))] fn test_separate_flash_active_page_biggest() { const STATE: Partition = Partition::new(2048, 4096); const ACTIVE: Partition = Partition::new(4096, 16384); @@ -1074,6 +1363,7 @@ mod tests { } #[test] + #[cfg(not(feature = "_verify"))] fn test_separate_flash_dfu_page_biggest() { const STATE: Partition = Partition::new(2048, 4096); const ACTIVE: Partition = Partition::new(4096, 16384); @@ -1133,6 +1423,55 @@ mod tests { assert_partitions(ACTIVE, DFU, STATE, 4096, 4); } + #[test] + #[cfg(feature = "_verify")] + fn test_verify() { + // The following key setup is based on: + // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example + + use ed25519_dalek::Keypair; + use rand::rngs::OsRng; + + let mut csprng = OsRng {}; + let keypair: Keypair = Keypair::generate(&mut csprng); + + use ed25519_dalek::{Digest, Sha512, Signature, Signer}; + let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU."; + let mut digest = Sha512::new(); + digest.update(&firmware); + let message = digest.finalize(); + let signature: Signature = keypair.sign(&message); + + use ed25519_dalek::PublicKey; + let public_key: PublicKey = keypair.public; + + // Setup flash + + const STATE: Partition = Partition::new(0, 4096); + const DFU: Partition = Partition::new(4096, 8192); + let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); + + let firmware_len = firmware.len(); + + let mut write_buf = [0; 4096]; + write_buf[0..firmware_len].copy_from_slice(firmware); + NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); + + // On with the test + + let mut updater = FirmwareUpdater::new(DFU, STATE); + + let mut aligned = [0; 4]; + + assert!(block_on(updater.verify_and_mark_updated( + &mut flash, + &public_key.to_bytes(), + &signature.to_bytes(), + firmware_len, + &mut aligned, + )) + .is_ok()); + } struct MemFlash([u8; SIZE]); impl NorFlash @@ -1171,7 +1510,7 @@ mod tests { impl ReadNorFlash for MemFlash { - const READ_SIZE: usize = 4; + const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { let len = buf.len(); @@ -1194,7 +1533,7 @@ mod tests { impl AsyncReadNorFlash for MemFlash { - const READ_SIZE: usize = 4; + const READ_SIZE: usize = 1; type ReadFuture<'a> = impl Future> + 'a; fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 9679bbc5..88899325 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -9,6 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } +embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -19,3 +20,7 @@ embedded-hal = { version = "0.2.6" } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" + +[features] +ed25519-dalek = ["embassy-boot/ed25519-dalek"] +ed25519-salty = ["embassy-boot/ed25519-salty"] \ No newline at end of file diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index 5d45f629..9d6d2033 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md @@ -22,7 +22,7 @@ cp memory-bl.x ../../bootloader/nrf/memory.x # Flash bootloader cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA # Build 'b' -cargo build --release --bin b +cargo build --release --bin b --features embassy-nrf/nrf52840 # Generate binary for 'b' cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin ``` From 7ecb05ff77ba8cd87d87c1c6bc3b8c1a1624ad15 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 13 Jan 2023 12:08:25 -0600 Subject: [PATCH 283/374] usb: allow setting the interface string for interface alt settings --- embassy-usb/src/builder.rs | 19 +++++++++++++++---- embassy-usb/src/class/cdc_acm.rs | 4 ++-- embassy-usb/src/class/cdc_ncm/mod.rs | 6 +++--- embassy-usb/src/class/hid.rs | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 87a8333b..785dc6f8 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -306,14 +306,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0. /// /// The first alternate setting, with number 0, is the default one. - pub fn alt_setting(&mut self, class: u8, subclass: u8, protocol: u8) -> InterfaceAltBuilder<'_, 'd, D> { + pub fn alt_setting( + &mut self, + class: u8, + subclass: u8, + protocol: u8, + interface_string: Option, + ) -> InterfaceAltBuilder<'_, 'd, D> { let number = self.next_alt_setting_number; self.next_alt_setting_number += 1; self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1; - self.builder - .config_descriptor - .interface_alt(self.interface_number, number, class, subclass, protocol, None); + self.builder.config_descriptor.interface_alt( + self.interface_number, + number, + class, + subclass, + protocol, + interface_string, + ); InterfaceAltBuilder { builder: self.builder, diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 84db2062..09d7f774 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -171,7 +171,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { iface.handler(control); let comm_if = iface.interface_number(); let data_if = u8::from(comm_if) + 1; - let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); alt.descriptor( CS_INTERFACE, @@ -205,7 +205,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { // Data interface let mut iface = func.interface(); let data_if = iface.interface_number(); - let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 4954a65b..d946eb11 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -254,7 +254,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { mac_addr_str: [0; 12], })); let comm_if = iface.interface_number(); - let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); alt.descriptor( CS_INTERFACE, @@ -304,8 +304,8 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { let mut iface = func.interface(); iface.handler(state.data_control.write(DataControl {})); let data_if = iface.interface_number(); - let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB); - let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB); + let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); + let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index b967aba0..12e49b7d 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -101,7 +101,7 @@ fn build<'d, D: Driver<'d>>( let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); let mut iface = func.interface(); iface.handler(control); - let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); // HID descriptor alt.descriptor( From 816b214403b7ce524cbb6ce85732df70f5b8f19e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 14 Jan 2023 07:12:43 +0100 Subject: [PATCH 284/374] Only implement Write --- embassy-stm32/src/usart/mod.rs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 1c13d9ec..1f50e5dc 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -912,7 +912,7 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::{Read, Write}; + use embedded_io::asynch::Read; use embedded_io::Io; use super::*; @@ -924,16 +924,6 @@ mod eio { type Error = Error; } - impl Read for Uart<'_, T, TxDma, RxDma> - where - T: BasicInstance, - RxDma: super::RxDma, - { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_until_idle(buf).await - } - } - impl Write for Uart<'_, T, TxDma, RxDma> where T: BasicInstance, @@ -949,23 +939,6 @@ mod eio { } } - impl Io for UartRx<'_, T, RxDma> - where - T: BasicInstance, - { - type Error = Error; - } - - impl Read for UartRx<'_, T, RxDma> - where - T: BasicInstance, - RxDma: super::RxDma, - { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_until_idle(buf).await - } - } - impl Io for UartTx<'_, T, TxDma> where T: BasicInstance, From 16590732f8708f4f55cbb9bc126bf5218b5e3c58 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 14 Jan 2023 07:13:29 +0100 Subject: [PATCH 285/374] Update mod.rs --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 1f50e5dc..d71aa61a 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -912,7 +912,7 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::Read; + use embedded_io::asynch::Write; use embedded_io::Io; use super::*; From 868d01889b7976ca4e4d2d775da19a1392208579 Mon Sep 17 00:00:00 2001 From: huntc Date: Sat, 14 Jan 2023 17:36:22 +1100 Subject: [PATCH 286/374] Documentation on verifying firmware The documentation has been enhanced to describe the verification of firmware with the firmware updater. Examples have also been provided that describe how keys can be generated and how firmware can be signed. --- docs/modules/ROOT/pages/bootloader.adoc | 50 ++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index b50de5ab..b7215e52 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -6,7 +6,7 @@ The bootloader can be used either as a library or be flashed directly if you are By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself. -The bootloader supports both internal and external flash by relying on the `embedded-storage` traits. +The bootloader supports both internal and external flash by relying on the `embedded-storage` traits. The bootloader optionally supports the verification of firmware that has been digitally signed (recommended). == Hardware support @@ -44,3 +44,51 @@ The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. + +=== FirmwareUpdater + +The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call. + +=== Verification + +The bootloader supports the verification of firmware that has been flashed to the DFU partition. Verification requires that firmware has been signed digitally using link:https://ed25519.cr.yp.to/[`ed25519`] signatures. With verification enabled, the `FirmwareUpdater::verify_and_mark_updated` method is called in place of `mark_updated`. A public key and signature are required, along with the actual length of the firmware that has been flashed. If verification fails then the firmware will not be marked as updated and therefore be rejected. + +Signatures are normally conveyed with the firmware to be updated and not written to flash. How signatures are provided is a firmware responsibility. + +To enable verification use either the `ed25519-dalek` or `ed25519-salty` features when depending on the `embassy-boot` crate. We recommend `ed25519-salty` at this time due to its small size. + +==== Tips on keys and signing with ed25519 + +Ed25519 is a public key signature system where you are responsible for keeping the private key secure. We recommend embedding the *public* key in your program so that it can be easily passed to `verify_and_mark_updated`. An example declaration of the public key in your firmware: + +[source, rust] +---- +static PUBLIC_SIGNING_KEY: &[u8] = include_bytes!("key.pub"); +---- + +Signatures are often conveyed along with firmware by appending them. + +Ed25519 keys can be generated by a variety of tools. We recommend link:https://man.openbsd.org/signify[`signify`] as it is in wide use to sign and verify OpenBSD distributions, and is straightforward to use. + +The following set of Bash commands can be used to generate public and private keys on Unix platforms, and also generate a local `key.pub` file with the `signify` file headers removed. Declare a `SECRETS_DIR` environment variable in a secure location. + +[source, bash] +---- +signify -G -n -p $SECRETS_DIR/key.pub -s $SECRETS_DIR/key.sec +tail -n1 $SECRETS_DIR/key.pub | base64 -d -i - | dd ibs=10 skip=1 > key.pub +chmod 700 $SECRETS_DIR/key.sec +export SECRET_SIGNING_KEY=$(tail -n1 $SECRETS_DIR/key.sec) +---- + +Then, to sign your firmware given a declaration of `FIRMWARE_DIR` and a firmware filename of `myfirmware`: + +[source, bash] +---- +shasum -a 512 -b $FIRMWARE_DIR/myfirmware > $SECRETS_DIR/message.txt +cat $SECRETS_DIR/message.txt | dd ibs=128 count=1 | xxd -p -r > $SECRETS_DIR/message.txt +signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig +cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed +tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed +---- + +Remember, guard the `$SECRETS_DIR/key.sec` key as compromising it means that another party can sign your firmware. \ No newline at end of file From 2a349afea791d2cb262497433a2ad24c3010a696 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 17 Jan 2023 18:54:23 +0100 Subject: [PATCH 287/374] stm32: add stm32c0 support. --- ci.sh | 2 +- ci_stable.sh | 2 +- embassy-stm32/Cargo.toml | 21 ++-- embassy-stm32/src/exti.rs | 16 +-- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/rcc/c0.rs | 233 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 5 +- stm32-data | 2 +- stm32-metapac-gen/Cargo.toml | 2 +- stm32-metapac/Cargo.toml | 21 ++-- 10 files changed, 277 insertions(+), 29 deletions(-) create mode 100644 embassy-stm32/src/rcc/c0.rs diff --git a/ci.sh b/ci.sh index 30e664a2..fc802f85 100755 --- a/ci.sh +++ b/ci.sh @@ -76,7 +76,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ diff --git a/ci_stable.sh b/ci_stable.sh index 60ddb659..b4b0b83e 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -36,7 +36,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 67996cca..d9c1f6dc 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -19,6 +19,7 @@ flavors = [ { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f42.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, @@ -110,6 +111,19 @@ unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. +stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] +stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] +stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] +stm32c011j4 = [ "stm32-metapac/stm32c011j4" ] +stm32c011j6 = [ "stm32-metapac/stm32c011j6" ] +stm32c031c4 = [ "stm32-metapac/stm32c031c4" ] +stm32c031c6 = [ "stm32-metapac/stm32c031c6" ] +stm32c031f4 = [ "stm32-metapac/stm32c031f4" ] +stm32c031f6 = [ "stm32-metapac/stm32c031f6" ] +stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] +stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] +stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] +stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] stm32f030cc = [ "stm32-metapac/stm32f030cc" ] @@ -1318,11 +1332,9 @@ stm32u575zi = [ "stm32-metapac/stm32u575zi" ] stm32u585ai = [ "stm32-metapac/stm32u585ai" ] stm32u585ci = [ "stm32-metapac/stm32u585ci" ] stm32u585oi = [ "stm32-metapac/stm32u585oi" ] -stm32u585qe = [ "stm32-metapac/stm32u585qe" ] stm32u585qi = [ "stm32-metapac/stm32u585qi" ] stm32u585ri = [ "stm32-metapac/stm32u585ri" ] stm32u585vi = [ "stm32-metapac/stm32u585vi" ] -stm32u585ze = [ "stm32-metapac/stm32u585ze" ] stm32u585zi = [ "stm32-metapac/stm32u585zi" ] stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] @@ -1340,7 +1352,6 @@ stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ] stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ] stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ] stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ] -stm32wb5mmg = [ "stm32-metapac/stm32wb5mmg" ] stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ] stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ] stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ] @@ -1349,8 +1360,6 @@ stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ] stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ] stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ] stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ] -stm32wl55uc-cm4 = [ "stm32-metapac/stm32wl55uc-cm4" ] -stm32wl55uc-cm0p = [ "stm32-metapac/stm32wl55uc-cm0p" ] stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] @@ -1363,6 +1372,4 @@ stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] -stm32wle5u8 = [ "stm32-metapac/stm32wle5u8" ] -stm32wle5ub = [ "stm32-metapac/stm32wle5ub" ] # END GENERATED FEATURES diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index f9078581..c9c3ef62 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti { EXTI } -#[cfg(not(any(exti_g0, exti_l5, gpio_v1, exti_u5)))] +#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))] fn exticr_regs() -> pac::syscfg::Syscfg { pac::SYSCFG } -#[cfg(any(exti_g0, exti_l5, exti_u5))] +#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] fn exticr_regs() -> pac::exti::Exti { EXTI } @@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio { } pub unsafe fn on_irq() { - #[cfg(not(any(exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] let bits = EXTI.pr(0).read().0; - #[cfg(any(exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; // Mask all the channels that fired. @@ -53,9 +53,9 @@ pub unsafe fn on_irq() { } // Clear pending - #[cfg(not(any(exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] EXTI.pr(0).write_value(Lines(bits)); - #[cfg(any(exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); @@ -212,9 +212,9 @@ impl<'a> ExtiInputFuture<'a> { EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); // clear pending bit - #[cfg(not(any(exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] EXTI.pr(0).write(|w| w.set_line(pin, true)); - #[cfg(any(exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] { EXTI.rpr(0).write(|w| w.set_line(pin, true)); EXTI.fpr(0).write(|w| w.set_line(pin, true)); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 610c2488..eeaa04f6 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -121,7 +121,7 @@ pub fn init(config: Config) -> Peripherals { #[cfg(dbgmcu)] if config.enable_debug_during_sleep { crate::pac::DBGMCU.cr().modify(|cr| { - #[cfg(any(dbgmcu_f0, dbgmcu_g0, dbgmcu_u5))] + #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))] { cr.set_dbg_stop(true); cr.set_dbg_standby(true); diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs new file mode 100644 index 00000000..6c7b3664 --- /dev/null +++ b/embassy-stm32/src/rcc/c0.rs @@ -0,0 +1,233 @@ +use crate::pac::flash::vals::Latency; +use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::{FLASH, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; + +/// HSI speed +pub const HSI_FREQ: Hertz = Hertz(48_000_000); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +/// System clock mux source +#[derive(Clone, Copy)] +pub enum ClockSrc { + HSE(Hertz), + HSI(HSIPrescaler), + LSI, +} + +#[derive(Clone, Copy)] +pub enum HSIPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl Into for HSIPrescaler { + fn into(self) -> Hsidiv { + match self { + HSIPrescaler::NotDivided => Hsidiv::DIV1, + HSIPrescaler::Div2 => Hsidiv::DIV2, + HSIPrescaler::Div4 => Hsidiv::DIV4, + HSIPrescaler::Div8 => Hsidiv::DIV8, + HSIPrescaler::Div16 => Hsidiv::DIV16, + HSIPrescaler::Div32 => Hsidiv::DIV32, + HSIPrescaler::Div64 => Hsidiv::DIV64, + HSIPrescaler::Div128 => Hsidiv::DIV128, + } + } +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div64, + Div128, + Div256, + Div512, +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl Into for APBPrescaler { + fn into(self) -> Ppre { + match self { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl Into for AHBPrescaler { + fn into(self) -> Hpre { + match self { + AHBPrescaler::NotDivided => Hpre::DIV1, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +/// Clocks configutation +pub struct Config { + pub mux: ClockSrc, + pub ahb_pre: AHBPrescaler, + pub apb_pre: APBPrescaler, +} + +impl Default for Config { + #[inline] + fn default() -> Config { + Config { + mux: ClockSrc::HSI(HSIPrescaler::NotDivided), + ahb_pre: AHBPrescaler::NotDivided, + apb_pre: APBPrescaler::NotDivided, + } + } +} + +pub(crate) unsafe fn init(config: Config) { + let (sys_clk, sw) = match config.mux { + ClockSrc::HSI(div) => { + // Enable HSI + let div: Hsidiv = div.into(); + RCC.cr().write(|w| { + w.set_hsidiv(div); + w.set_hsion(true) + }); + while !RCC.cr().read().hsirdy() {} + + (HSI_FREQ.0 >> div.0, Sw::HSI) + } + ClockSrc::HSE(freq) => { + // Enable HSE + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + + (freq.0, Sw::HSE) + } + ClockSrc::LSI => { + // Enable LSI + RCC.csr2().write(|w| w.set_lsion(true)); + while !RCC.csr2().read().lsirdy() {} + (LSI_FREQ.0, Sw::LSI) + } + }; + + // Determine the flash latency implied by the target clock speed + // RM0454 § 3.3.4: + let target_flash_latency = if sys_clk <= 24_000_000 { + Latency::WS0 + } else { + Latency::WS1 + }; + + // Increase the number of cycles we wait for flash if the new value is higher + // There's no harm in waiting a little too much before the clock change, but we'll + // crash immediately if we don't wait enough after the clock change + let mut set_flash_latency_after = false; + FLASH.acr().modify(|w| { + // Is the current flash latency less than what we need at the new SYSCLK? + if w.latency().0 <= target_flash_latency.0 { + // We must increase the number of wait states now + w.set_latency(target_flash_latency) + } else { + // We may decrease the number of wait states later + set_flash_latency_after = true; + } + + // RM0490 § 3.3.4: + // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register + // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the + // > Flash memory. + // + // Enable flash prefetching if we have at least one wait state, and disable it otherwise. + w.set_prften(target_flash_latency.0 > 0); + }); + + if !set_flash_latency_after { + // Spin until the effective flash latency is compatible with the clock change + while FLASH.acr().read().latency().0 < target_flash_latency.0 {} + } + + // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once + let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into()); + RCC.cfgr().modify(|w| { + w.set_sw(sw); + w.set_hpre(hpre); + w.set_ppre(ppre); + }); + + if set_flash_latency_after { + // We can make the flash require fewer wait states + // Spin until the SYSCLK changes have taken effect + loop { + let cfgr = RCC.cfgr().read(); + if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre { + break; + } + } + + // Set the flash latency to require fewer wait states + FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); + } + + let ahb_div = match config.ahb_pre { + AHBPrescaler::NotDivided => 1, + AHBPrescaler::Div2 => 2, + AHBPrescaler::Div4 => 4, + AHBPrescaler::Div8 => 8, + AHBPrescaler::Div16 => 16, + AHBPrescaler::Div64 => 64, + AHBPrescaler::Div128 => 128, + AHBPrescaler::Div256 => 256, + AHBPrescaler::Div512 => 512, + }; + let ahb_freq = sys_clk / ahb_div; + + let (apb_freq, apb_tim_freq) = match config.apb_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + pre => { + let pre: Ppre = pre.into(); + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq / pre as u32; + (freq, freq * 2) + } + }; + + set_freqs(Clocks { + sys: Hertz(sys_clk), + ahb1: Hertz(ahb_freq), + apb1: Hertz(apb_freq), + apb1_tim: Hertz(apb_tim_freq), + }); +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 1b1180c0..0a52089d 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -10,6 +10,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_f3, path = "f3.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")] +#[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_g0, path = "g0.rs")] #[cfg_attr(rcc_g4, path = "g4.rs")] #[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")] @@ -30,9 +31,9 @@ pub struct Clocks { // APB pub apb1: Hertz, pub apb1_tim: Hertz, - #[cfg(not(rcc_g0))] + #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2: Hertz, - #[cfg(not(rcc_g0))] + #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2_tim: Hertz, #[cfg(any(rcc_wl5, rcc_wle, rcc_u5))] pub apb3: Hertz, diff --git a/stm32-data b/stm32-data index 844793fc..96decdd6 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 844793fc3da2ba3f12ab6a69b78cd8e6fb5497b4 +Subproject commit 96decdd6114d78813c1f748fb878a45e1b03bf73 diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml index 9598a594..6d136ba6 100644 --- a/stm32-metapac-gen/Cargo.toml +++ b/stm32-metapac-gen/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] regex = "1.5.4" -chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "28ffa8a19d84914089547f52900ffb5877a5dc23" } +chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "1d9e0a39a6acc291e50cabc4ed617a87f06d5e89" } serde = { version = "1.0.130", features = [ "derive" ] } serde_json = "1.0.87" serde_yaml = "0.8.21" diff --git a/stm32-metapac/Cargo.toml b/stm32-metapac/Cargo.toml index 9d5aba0c..2605cf3d 100644 --- a/stm32-metapac/Cargo.toml +++ b/stm32-metapac/Cargo.toml @@ -27,6 +27,7 @@ flavors = [ { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, @@ -67,6 +68,19 @@ memory-x = [] # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. +stm32c011d6 = [] +stm32c011f4 = [] +stm32c011f6 = [] +stm32c011j4 = [] +stm32c011j6 = [] +stm32c031c4 = [] +stm32c031c6 = [] +stm32c031f4 = [] +stm32c031f6 = [] +stm32c031g4 = [] +stm32c031g6 = [] +stm32c031k4 = [] +stm32c031k6 = [] stm32f030c6 = [] stm32f030c8 = [] stm32f030cc = [] @@ -1275,11 +1289,9 @@ stm32u575zi = [] stm32u585ai = [] stm32u585ci = [] stm32u585oi = [] -stm32u585qe = [] stm32u585qi = [] stm32u585ri = [] stm32u585vi = [] -stm32u585ze = [] stm32u585zi = [] stm32wb10cc = [] stm32wb15cc = [] @@ -1297,7 +1309,6 @@ stm32wb55vc = [] stm32wb55ve = [] stm32wb55vg = [] stm32wb55vy = [] -stm32wb5mmg = [] stm32wl54cc-cm4 = [] stm32wl54cc-cm0p = [] stm32wl54jc-cm4 = [] @@ -1306,8 +1317,6 @@ stm32wl55cc-cm4 = [] stm32wl55cc-cm0p = [] stm32wl55jc-cm4 = [] stm32wl55jc-cm0p = [] -stm32wl55uc-cm4 = [] -stm32wl55uc-cm0p = [] stm32wle4c8 = [] stm32wle4cb = [] stm32wle4cc = [] @@ -1320,6 +1329,4 @@ stm32wle5cc = [] stm32wle5j8 = [] stm32wle5jb = [] stm32wle5jc = [] -stm32wle5u8 = [] -stm32wle5ub = [] # END GENERATED FEATURES From 355761fd68a238033bcb3441d949bf5115b1a84a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 17 Jan 2023 18:54:43 +0100 Subject: [PATCH 288/374] stm32: add stm32c0 examples. --- ci.sh | 1 + examples/stm32c0/.cargo/config.toml | 9 +++++++++ examples/stm32c0/Cargo.toml | 21 +++++++++++++++++++ examples/stm32c0/build.rs | 5 +++++ examples/stm32c0/src/bin/blinky.rs | 27 +++++++++++++++++++++++++ examples/stm32c0/src/bin/button.rs | 25 +++++++++++++++++++++++ examples/stm32c0/src/bin/button_exti.rs | 27 +++++++++++++++++++++++++ 7 files changed, 115 insertions(+) create mode 100644 examples/stm32c0/.cargo/config.toml create mode 100644 examples/stm32c0/Cargo.toml create mode 100644 examples/stm32c0/build.rs create mode 100644 examples/stm32c0/src/bin/blinky.rs create mode 100644 examples/stm32c0/src/bin/button.rs create mode 100644 examples/stm32c0/src/bin/button_exti.rs diff --git a/ci.sh b/ci.sh index fc802f85..323e752d 100755 --- a/ci.sh +++ b/ci.sh @@ -98,6 +98,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ + --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ diff --git a/examples/stm32c0/.cargo/config.toml b/examples/stm32c0/.cargo/config.toml new file mode 100644 index 00000000..eb07f619 --- /dev/null +++ b/examples/stm32c0/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32G071C8Rx with your chip as listed in `probe-run --list-chips` +runner = "probe-rs-cli run --speed 100 --chip STM32c031c6tx" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml new file mode 100644 index 00000000..0095a680 --- /dev/null +++ b/examples/stm32c0/Cargo.toml @@ -0,0 +1,21 @@ +[package] +edition = "2021" +name = "embassy-stm32c0-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +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", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32c0/build.rs b/examples/stm32c0/build.rs new file mode 100644 index 00000000..8cd32d7e --- /dev/null +++ b/examples/stm32c0/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32c0/src/bin/blinky.rs b/examples/stm32c0/src/bin/blinky.rs new file mode 100644 index 00000000..8a65b069 --- /dev/null +++ b/examples/stm32c0/src/bin/blinky.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} diff --git a/examples/stm32c0/src/bin/button.rs b/examples/stm32c0/src/bin/button.rs new file mode 100644 index 00000000..72a3f5cb --- /dev/null +++ b/examples/stm32c0/src/bin/button.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let button = Input::new(p.PC13, Pull::Up); + + loop { + if button.is_high() { + info!("high"); + } else { + info!("low"); + } + } +} diff --git a/examples/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs new file mode 100644 index 00000000..ef32d4c4 --- /dev/null +++ b/examples/stm32c0/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let button = Input::new(p.PC13, Pull::Up); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} From d2f2b451d084448d442c6f9cb1c6b9f94a4fd447 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:29:49 +0200 Subject: [PATCH 289/374] stm32/usb_otg: implement endpoint wait_enabled --- embassy-stm32/src/usb_otg/usb.rs | 38 ++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 2d9b613e..504a90f2 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -791,6 +791,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { w.set_usbaep(enabled); }) }); + + // Wake `Endpoint::wait_enabled()` + T::state().ep_out_wakers[ep_addr.index()].wake(); } Direction::In => { // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW @@ -807,6 +810,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { w.set_usbaep(enabled); }) }); + + // Wake `Endpoint::wait_enabled()` + T::state().ep_in_wakers[ep_addr.index()].wake(); } } } @@ -1031,7 +1037,21 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> { &self.info } - async fn wait_enabled(&mut self) {} + async fn wait_enabled(&mut self) { + poll_fn(|cx| { + let ep_index = self.info.addr.index(); + + T::state().ep_in_wakers[ep_index].register(cx.waker()); + + // SAFETY: atomic read without side effects + if unsafe { T::regs().diepctl(ep_index).read().usbaep() } { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } } impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { @@ -1039,7 +1059,21 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { &self.info } - async fn wait_enabled(&mut self) {} + async fn wait_enabled(&mut self) { + poll_fn(|cx| { + let ep_index = self.info.addr.index(); + + T::state().ep_out_wakers[ep_index].register(cx.waker()); + + // SAFETY: atomic read without side effects + if unsafe { T::regs().doepctl(ep_index).read().usbaep() } { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } } impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { From f07e59b24a2fff6dfbfd52ac5f55fb2667dae0d8 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:31:28 +0200 Subject: [PATCH 290/374] stm32/usb_otg: prevent writes on disabled endpoint --- embassy-stm32/src/usb_otg/usb.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 504a90f2..48c3ec01 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1126,6 +1126,8 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + trace!("write ep={} data={}", self.info.addr, buf); + if buf.len() > self.info.max_packet_size as usize { return Err(EndpointError::BufferOverflow); } @@ -1134,18 +1136,21 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { let index = self.info.addr.index(); let state = T::state(); - // Wait for previous transfer to complete + // Wait for previous transfer to complete and check if endpoint is disabled poll_fn(|cx| { state.ep_in_wakers[index].register(cx.waker()); // SAFETY: atomic read with no side effects - if unsafe { r.diepctl(index).read().epena() } { - Poll::Pending + let diepctl = unsafe { r.diepctl(index).read() }; + if !diepctl.usbaep() { + Poll::Ready(Err(EndpointError::Disabled)) + } else if !diepctl.epena() { + Poll::Ready(Ok(())) } else { - Poll::Ready(()) + Poll::Pending } }) - .await; + .await?; if buf.len() > 0 { poll_fn(|cx| { @@ -1201,7 +1206,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; } - trace!("WRITE OK"); + trace!("write done ep={}", self.info.addr); Ok(()) } @@ -1239,8 +1244,8 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { // Clear NAK to indicate we are ready to receive more data T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { w.set_cnak(true); - }) - }; + }); + } trace!("SETUP received: {:?}", data); Poll::Ready(data) From 7d34f4f538f88d84d499173e15f63e676d4af9c0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:37:02 +0200 Subject: [PATCH 291/374] stm32/usb_otg: Add F4 usb_ethernet example --- examples/stm32f4/Cargo.toml | 6 +- examples/stm32f4/src/bin/usb_ethernet.rs | 169 +++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f4/src/bin/usb_ethernet.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 252d6085..e2b17bfc 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -4,13 +4,13 @@ name = "embassy-stm32f4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" - [dependencies] 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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -27,5 +27,9 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" +[[bin]] +name = "usb_ethernet" +required-features = ["embassy-net"] + [profile.release] debug = 2 diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs new file mode 100644 index 00000000..cf2885ae --- /dev/null +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -0,0 +1,169 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::Driver; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; +use embassy_usb::{Builder, UsbDevice}; +use embedded_io::asynch::Write; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +const MTU: usize = 1514; + +#[embassy_executor::task] +async fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! { + device.run().await +} + +#[embassy_executor::task] +async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { + class.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(48)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let ep_out_buffer = &mut singleton!([0; 256])[..]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-Ethernet example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for Windows support. + config.composite_with_iads = true; + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + + // Create embassy-usb DeviceBuilder using the driver and config. + let mut builder = Builder::new( + driver, + config, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + None, + ); + + // Our MAC addr. + let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; + // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. + let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; + + // Create classes on the builder. + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); + + // Build the builder. + let usb = builder.build(); + + unwrap!(spawner.spawn(usb_task(usb))); + + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); + + let config = embassy_net::ConfigStrategy::Dhcp; + //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Generate random seed + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + unwrap!(rng.async_fill_bytes(&mut seed).await); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + config, + singleton!(StackResources::<1, 2, 8>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From b1203bf036454bf28903fc079086e3cc93a18446 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 03:06:32 +0200 Subject: [PATCH 292/374] stm32/usb_otg: fix core formatter --- embassy-stm32/src/usb_otg/usb.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 48c3ec01..fd2f6707 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1126,7 +1126,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { - trace!("write ep={} data={}", self.info.addr, buf); + trace!("write ep={:?} data={:?}", self.info.addr, buf); if buf.len() > self.info.max_packet_size as usize { return Err(EndpointError::BufferOverflow); @@ -1206,7 +1206,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; } - trace!("write done ep={}", self.info.addr); + trace!("write done ep={:?}", self.info.addr); Ok(()) } From 6ab4ecaf8379820984242a4bfb6ff4421c01b11e Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 18 Jan 2023 14:17:50 +1100 Subject: [PATCH 293/374] Stop sampling when exiting the Saadc methods Prior to this commit, the onDrop function was being dropped immediately and not on exiting the Saadc sampling methods. --- embassy-nrf/src/saadc.rs | 12 +++++++++--- embassy-stm32/src/i2c/v2.rs | 10 ++++++++-- embassy-stm32/src/usart/mod.rs | 10 +++++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index d1c82423..4592d468 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -225,7 +225,7 @@ impl<'d, const N: usize> Saadc<'d, N> { /// also cause the sampling to be stopped. pub async fn sample(&mut self, buf: &mut [i16; N]) { // In case the future is dropped, stop the task and wait for it to end. - OnDrop::new(Self::stop_sampling_immediately); + let on_drop = OnDrop::new(Self::stop_sampling_immediately); let r = Self::regs(); @@ -258,6 +258,8 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; + + drop(on_drop); } /// Continuous sampling with double buffers. @@ -335,7 +337,7 @@ impl<'d, const N: usize> Saadc<'d, N> { S: FnMut(&[[i16; N]]) -> SamplerState, { // In case the future is dropped, stop the task and wait for it to end. - OnDrop::new(Self::stop_sampling_immediately); + let on_drop = OnDrop::new(Self::stop_sampling_immediately); let r = Self::regs(); @@ -382,7 +384,7 @@ impl<'d, const N: usize> Saadc<'d, N> { let mut current_buffer = 0; // Wait for events and complete when the sampler indicates it has had enough. - poll_fn(|cx| { + let r = poll_fn(|cx| { let r = Self::regs(); WAKER.register(cx.waker()); @@ -419,6 +421,10 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; + + drop(on_drop); + + r } // Stop sampling and wait for it to stop in a blocking fashion diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4622635b..06ff07b2 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -483,7 +483,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; - let _on_drop = OnDrop::new(|| { + let on_drop = OnDrop::new(|| { let regs = T::regs(); unsafe { regs.cr1().modify(|w| { @@ -542,6 +542,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.wait_tc(&check_timeout)?; self.master_stop(); } + + drop(on_drop); + Ok(()) } @@ -580,7 +583,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; - let _on_drop = OnDrop::new(|| { + let on_drop = OnDrop::new(|| { let regs = T::regs(); unsafe { regs.cr1().modify(|w| { @@ -629,6 +632,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // This should be done already self.wait_tc(&check_timeout)?; self.master_stop(); + + drop(on_drop); + Ok(()) } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index d71aa61a..20f4eede 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -405,7 +405,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let r = T::regs(); // make sure USART state is restored to neutral state when this future is dropped - let _drop = OnDrop::new(move || { + let on_drop = OnDrop::new(move || { // defmt::trace!("Clear all USART interrupts and DMA Read Request"); // clear all interrupts and DMA Rx Request // SAFETY: only clears Rx related flags @@ -563,7 +563,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // wait for the first of DMA request or idle line detected to completes // select consumes its arguments // when transfer is dropped, it will stop the DMA request - match select(transfer, idle).await { + let r = match select(transfer, idle).await { // DMA transfer completed first Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), @@ -572,7 +572,11 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // error occurred Either::Second(Err(e)) => Err(e), - } + }; + + drop(on_drop); + + r } async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result From 83af513424b3fcff98ca7bc12cc4e24261eb08e7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 19 Jan 2023 13:29:51 +0800 Subject: [PATCH 294/374] rp gpio: make pin_bank() inline This allows set_high() etc to be inlined, toggling pins should be much faster. --- embassy-rp/src/gpio.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ec05de61..4abb9839 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -743,6 +743,7 @@ macro_rules! impl_pin { ($name:ident, $bank:expr, $pin_num:expr) => { impl Pin for peripherals::$name {} impl sealed::Pin for peripherals::$name { + #[inline] fn pin_bank(&self) -> u8 { ($bank as u8) * 32 + $pin_num } From 2eae12b7f1cbe38d850ebf30f23a776323815af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Wed, 18 Jan 2023 09:56:38 +0100 Subject: [PATCH 295/374] Update smoltcp to the newest master --- embassy-net/Cargo.toml | 2 +- embassy-net/src/device.rs | 13 +++++++------ embassy-net/src/lib.rs | 30 ++++++++++-------------------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 9214fd17..a2ef3b4f 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -51,7 +51,7 @@ atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" git = "https://github.com/smoltcp-rs/smoltcp" -rev = "b7a7c4b1c56e8d4c2524c1e3a056c745a13cc09f" +rev = "7b631c27cefe9b29e322a507c076b4156f477ee2" default-features = false features = [ "proto-ipv4", diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 44f7dc7b..d0c8a62d 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -2,6 +2,7 @@ use core::task::Context; use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken}; use smoltcp::phy; +use smoltcp::time::Instant; pub(crate) struct DriverAdapter<'d, 'c, T> where @@ -19,14 +20,14 @@ where type RxToken<'a> = RxTokenAdapter> where Self: 'a; type TxToken<'a> = TxTokenAdapter> where Self: 'a; - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { self.inner .receive(self.cx.as_deref_mut().unwrap()) .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) } /// Construct a transmit token. - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) } @@ -76,9 +77,9 @@ impl phy::RxToken for RxTokenAdapter where T: RxToken, { - fn consume(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> smoltcp::Result, + F: FnOnce(&mut [u8]) -> R, { self.0.consume(|buf| f(buf)) } @@ -92,9 +93,9 @@ impl phy::TxToken for TxTokenAdapter where T: TxToken, { - fn consume(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> smoltcp::Result, + F: FnOnce(&mut [u8]) -> R, { self.0.consume(len, |buf| f(buf)) } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e4a4218e..757d3e27 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -23,11 +23,11 @@ use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; +#[cfg(feature = "medium-ethernet")] +use smoltcp::iface::Routes; #[cfg(feature = "dhcpv4")] use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; // smoltcp reexports @@ -45,25 +45,16 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -pub struct StackResources { - addresses: [IpCidr; ADDR], +pub struct StackResources { + addresses: [IpCidr; 5], sockets: [SocketStorage<'static>; SOCK], - - #[cfg(feature = "medium-ethernet")] - routes: [Option<(IpCidr, Route)>; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], } -impl StackResources { +impl StackResources { pub fn new() -> Self { Self { - addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], + addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); 5], sockets: [SocketStorage::EMPTY; SOCK], - #[cfg(feature = "medium-ethernet")] - routes: [None; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [None; NEIGHBOR], } } } @@ -105,21 +96,20 @@ impl Stack { pub fn new( mut device: D, config: ConfigStrategy, - resources: &'static mut StackResources, + resources: &'static mut StackResources, random_seed: u64, ) -> Self { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; let mut b = InterfaceBuilder::new(); - b = b.ip_addrs(&mut resources.addresses[..]); + b = b.ip_addrs(Vec::::from_iter(resources.addresses)); b = b.random_seed(random_seed); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); - b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); - b = b.routes(Routes::new(&mut resources.routes[..])); + b = b.routes(Routes::new()); } let iface = b.finalize(&mut DriverAdapter { @@ -266,7 +256,7 @@ impl Inner { cx: Some(cx), inner: &mut self.device, }; - if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { + if !s.iface.poll(timestamp, &mut smoldev, &mut s.sockets) { // If poll() returns error, it may not be done yet, so poll again later. cx.waker().wake_by_ref(); return; From 8f4fae9b36f017a8ab65491ef49b72499a9486dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Wed, 18 Jan 2023 10:10:33 +0100 Subject: [PATCH 296/374] Add smoltcp dhcp socket configuration --- embassy-net/src/lib.rs | 72 ++++++++++++++++------- examples/nrf52840/src/bin/usb_ethernet.rs | 11 +--- examples/rp/src/bin/usb_ethernet.rs | 11 +--- examples/std/Cargo.toml | 2 +- examples/std/src/bin/net.rs | 15 ++--- examples/std/src/bin/net_udp.rs | 13 ++-- examples/stm32f7/src/bin/eth.rs | 11 +--- examples/stm32h7/src/bin/eth.rs | 11 +--- examples/stm32h7/src/bin/eth_client.rs | 11 +--- examples/stm32l5/src/bin/usb_ethernet.rs | 11 +--- 10 files changed, 81 insertions(+), 87 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 757d3e27..c419ec1f 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -30,6 +30,8 @@ use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; +use smoltcp::socket::dhcpv4::RetryConfig; +use smoltcp::time::Duration; // smoltcp reexports pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] @@ -45,31 +47,53 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -pub struct StackResources { - addresses: [IpCidr; 5], +pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], } -impl StackResources { +impl StackResources { pub fn new() -> Self { Self { - addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); 5], sockets: [SocketStorage::EMPTY; SOCK], } } } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config { +pub struct StaticConfig { pub address: Ipv4Cidr, pub gateway: Option, pub dns_servers: Vec, } -pub enum ConfigStrategy { - Static(Config), +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DhcpConfig { + pub max_lease_duration: Option, + pub retry_config: RetryConfig, + /// Ignore NAKs. + pub ignore_naks: bool, + /// Server port config + pub server_port: u16, + /// Client port config + pub client_port: u16, +} + +impl Default for DhcpConfig { + fn default() -> Self { + Self { + max_lease_duration: Default::default(), + retry_config: Default::default(), + ignore_naks: Default::default(), + server_port: smoltcp::wire::DHCP_SERVER_PORT, + client_port: smoltcp::wire::DHCP_CLIENT_PORT, + } + } +} + +pub enum Config { + Static(StaticConfig), #[cfg(feature = "dhcpv4")] - Dhcp, + Dhcp(DhcpConfig), } pub struct Stack { @@ -80,7 +104,7 @@ pub struct Stack { struct Inner { device: D, link_up: bool, - config: Option, + config: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, } @@ -93,17 +117,16 @@ pub(crate) struct SocketStack { } impl Stack { - pub fn new( + pub fn new( mut device: D, - config: ConfigStrategy, - resources: &'static mut StackResources, + config: Config, + resources: &'static mut StackResources, random_seed: u64, ) -> Self { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; let mut b = InterfaceBuilder::new(); - b = b.ip_addrs(Vec::::from_iter(resources.addresses)); b = b.random_seed(random_seed); #[cfg(feature = "medium-ethernet")] @@ -136,10 +159,12 @@ impl Stack { }; match config { - ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), + Config::Static(config) => inner.apply_config(&mut socket, config), #[cfg(feature = "dhcpv4")] - ConfigStrategy::Dhcp => { - let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); + Config::Dhcp(config) => { + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + inner.apply_dhcp_config(&mut dhcp_socket, config); + let handle = socket.sockets.add(dhcp_socket); inner.dhcp_socket = Some(handle); } } @@ -170,7 +195,7 @@ impl Stack { self.with(|_s, i| i.config.is_some()) } - pub fn config(&self) -> Option { + pub fn config(&self) -> Option { self.with(|_s, i| i.config.clone()) } @@ -185,7 +210,7 @@ impl Stack { } impl SocketStack { - #[allow(clippy::absurd_extreme_comparisons)] + #[allow(clippy::absurd_extreme_comparisons, dead_code)] pub fn get_local_port(&mut self) -> u16 { let res = self.next_local_port; self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; @@ -194,7 +219,7 @@ impl SocketStack { } impl Inner { - fn apply_config(&mut self, s: &mut SocketStack, config: Config) { + fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfig) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -220,6 +245,13 @@ impl Inner { self.config = Some(config) } + fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { + socket.set_ignore_naks(config.ignore_naks); + socket.set_max_lease_duration(config.max_lease_duration); + socket.set_ports(config.server_port, config.client_port); + socket.set_retry_config(config.retry_config); + } + #[allow(unused)] // used only with dhcp fn unapply_config(&mut self, s: &mut SocketStack) { #[cfg(feature = "medium-ethernet")] @@ -280,7 +312,7 @@ impl Inner { None => {} Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), Some(dhcpv4::Event::Configured(config)) => { - let config = Config { + let config = StaticConfig { address: config.address, gateway: config.router, dns_servers: config.dns_servers, diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index e5f70452..1390bfc8 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -101,8 +101,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -115,12 +115,7 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index d0aec874..e9b72712 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -92,8 +92,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -103,12 +103,7 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 45b2a4a4..af1481e0 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -17,7 +17,7 @@ async-io = "1.6.0" env_logger = "0.9.0" futures = { version = "0.3.17" } log = "0.4.14" -nix = "0.22.1" +nix = "0.26.2" libc = "0.2.101" clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 9b1450b7..1ae39cac 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -1,9 +1,11 @@ #![feature(type_alias_impl_trait)] +use std::default::Default; + use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; -use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embedded_io::asynch::Write; use heapless::Vec; use log::*; @@ -48,13 +50,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - ConfigStrategy::Static(embassy_net::Config { + Config::Static(embassy_net::StaticConfig { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - ConfigStrategy::Dhcp + Config::Dhcp(Default::default()) }; // Generate random seed @@ -63,12 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 392a97f0..d1a8fe1e 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -3,7 +3,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::udp::UdpSocket; -use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; @@ -47,13 +47,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - ConfigStrategy::Static(embassy_net::Config { + Config::Static(embassy_net::StaticConfig { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - ConfigStrategy::Dhcp + Config::Dhcp(Default::default()) }; // Generate random seed @@ -62,12 +62,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 224cc202..6f33a4f8 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -69,20 +69,15 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 551325ca..ad7fcc5c 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -70,20 +70,15 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 61a08ae1..7d3904f6 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -71,20 +71,15 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::StaticConfig(embassy_net::Config { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index b49329ea..ff44c2fc 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -98,8 +98,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -110,12 +110,7 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); From 570ffab6703083f4e7f08a982afbf1c6de93b801 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 13:30:51 +0100 Subject: [PATCH 297/374] net: poll returning false is not an error. No need to repoll. --- embassy-net/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index c419ec1f..b8e766b8 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -288,11 +288,7 @@ impl Inner { cx: Some(cx), inner: &mut self.device, }; - if !s.iface.poll(timestamp, &mut smoldev, &mut s.sockets) { - // If poll() returns error, it may not be done yet, so poll again later. - cx.waker().wake_by_ref(); - return; - } + s.iface.poll(timestamp, &mut smoldev, &mut s.sockets); // Update link up let old_link_up = self.link_up; From fe15a7beee5f948b1e4c1cb8ab8e5cc85efb4662 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 14:40:58 +0100 Subject: [PATCH 298/374] net: allocate space for 2 sockets, needed for dhcp. --- examples/nrf52840/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/std/src/bin/net.rs | 2 +- examples/std/src/bin/net_udp.rs | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 1390bfc8..a8d53e46 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -115,7 +115,7 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index e9b72712..104b25d3 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -103,7 +103,7 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 1ae39cac..451850d9 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -65,7 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index d1a8fe1e..f1923f18 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -62,7 +62,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 6f33a4f8..571a6c1b 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -77,7 +77,7 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index ad7fcc5c..cb245c32 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -78,7 +78,7 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 7d3904f6..cce85a08 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -79,7 +79,7 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index ff44c2fc..e5a46b06 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); From 78c2c1709b8c111468fd7dd2ec1f3792d2fcea67 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 14:41:39 +0100 Subject: [PATCH 299/374] net: update smoltcp. --- embassy-net/Cargo.toml | 2 +- embassy-net/src/lib.rs | 42 ++++++++++++++++++++---------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index a2ef3b4f..15cbb595 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -51,7 +51,7 @@ atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" git = "https://github.com/smoltcp-rs/smoltcp" -rev = "7b631c27cefe9b29e322a507c076b4156f477ee2" +rev = "5740b765749b95c18aace5de8dc21cab75ba33d4" default-features = false features = [ "proto-ipv4", diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b8e766b8..8d0119f6 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -23,11 +23,9 @@ use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; -#[cfg(feature = "medium-ethernet")] -use smoltcp::iface::Routes; #[cfg(feature = "dhcpv4")] use smoltcp::iface::SocketHandle; -use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; +use smoltcp::iface::{Interface, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; use smoltcp::socket::dhcpv4::RetryConfig; @@ -111,7 +109,7 @@ struct Inner { pub(crate) struct SocketStack { pub(crate) sockets: SocketSet<'static>, - pub(crate) iface: Interface<'static>, + pub(crate) iface: Interface, pub(crate) waker: WakerRegistration, next_local_port: u16, } @@ -126,19 +124,20 @@ impl Stack { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; - let mut b = InterfaceBuilder::new(); - b = b.random_seed(random_seed); - + let mut iface_cfg = smoltcp::iface::Config::new(); + iface_cfg.random_seed = random_seed; #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { - b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); - b = b.routes(Routes::new()); + iface_cfg.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); } - let iface = b.finalize(&mut DriverAdapter { - inner: &mut device, - cx: None, - }); + let iface = Interface::new( + iface_cfg, + &mut DriverAdapter { + inner: &mut device, + cx: None, + }, + ); let sockets = SocketSet::new(&mut resources.sockets[..]); @@ -226,7 +225,13 @@ impl Inner { debug!("Acquired IP configuration:"); debug!(" IP address: {}", config.address); - self.set_ipv4_addr(s, config.address); + s.iface.update_ip_addrs(|addrs| { + if addrs.is_empty() { + addrs.push(IpCidr::Ipv4(config.address)).unwrap(); + } else { + addrs[0] = IpCidr::Ipv4(config.address); + } + }); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { @@ -258,7 +263,7 @@ impl Inner { let medium = self.device.capabilities().medium; debug!("Lost IP configuration"); - self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { s.iface.routes_mut().remove_default_ipv4_route(); @@ -266,13 +271,6 @@ impl Inner { self.config = None } - fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { - s.iface.update_ip_addrs(|addrs| { - let dest = addrs.iter_mut().next().unwrap(); - *dest = IpCidr::Ipv4(cidr); - }); - } - fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); From f604153f05e02fb07903f45b756fc9ddb6667d8a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 Jan 2023 16:31:04 +0100 Subject: [PATCH 300/374] stm32/rcc: print actual freqs on boot. --- embassy-stm32/src/rcc/mod.rs | 4 +++- embassy-stm32/src/time.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 0a52089d..d4bd3d6b 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -24,7 +24,8 @@ use crate::time::Hertz; mod _version; pub use _version::*; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Clocks { pub sys: Hertz, @@ -72,6 +73,7 @@ static mut CLOCK_FREQS: MaybeUninit = MaybeUninit::uninit(); /// /// Safety: Sets a mutable global. pub(crate) unsafe fn set_freqs(freqs: Clocks) { + debug!("rcc: {:?}", freqs); CLOCK_FREQS.as_mut_ptr().write(freqs); } diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 49140bbe..975517a4 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs @@ -2,6 +2,7 @@ /// Hertz #[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Hertz(pub u32); impl Hertz { From 0412d1922c6cca1200da0af54db3a5dbf60c035c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 21 Jan 2023 00:24:07 +0100 Subject: [PATCH 301/374] fix embedded-sdmmc integration. - Rename feature to `embedded-sdmmc`. - Move embedded-sdmmc fork repo to the embassy-rs org. - Remove unused features in the fork - Fix impl in embassy-stm32 - Add to CI so it doesn't break again. --- ci.sh | 2 +- embassy-stm32/Cargo.toml | 3 +-- embassy-stm32/src/sdmmc/mod.rs | 22 ++++++---------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/ci.sh b/ci.sh index 323e752d..b325e376 100755 --- a/ci.sh +++ b/ci.sh @@ -63,7 +63,7 @@ cargo batch \ --- 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 \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d9c1f6dc..10e5e48c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ cortex-m = "0.7.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand_core = "0.6.3" sdio-host = "0.5.0" -embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } +embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } @@ -77,7 +77,6 @@ stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features [features] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] -sdmmc-rs = ["embedded-sdmmc"] memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a52c65b9..3f07e0c0 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1532,7 +1532,7 @@ foreach_peripheral!( }; ); -#[cfg(feature = "sdmmc-rs")] +#[cfg(feature = "embedded-sdmmc")] mod sdmmc_rs { use core::future::Future; @@ -1540,7 +1540,7 @@ mod sdmmc_rs { use super::*; - impl<'d, T: Instance, P: Pins> BlockDevice for Sdmmc<'d, T, P> { + impl<'d, T: Instance, Dma: SdmmcDma> BlockDevice for Sdmmc<'d, T, Dma> { type Error = Error; type ReadFuture<'a> = impl Future> + 'a @@ -1558,19 +1558,14 @@ mod sdmmc_rs { _reason: &str, ) -> Self::ReadFuture<'a> { async move { - let card_capacity = self.card()?.card_type; - let inner = T::inner(); - let state = T::state(); let mut address = start_block_idx.0; for block in blocks.iter_mut() { let block: &mut [u8; 512] = &mut block.contents; // NOTE(unsafe) Block uses align(4) - let buf = unsafe { &mut *(block as *mut [u8; 512] as *mut [u32; 128]) }; - inner - .read_block(address, buf, card_capacity, state, self.config.data_transfer_timeout) - .await?; + let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; + self.read_block(address, block).await?; address += 1; } Ok(()) @@ -1579,19 +1574,14 @@ mod sdmmc_rs { fn write<'a>(&'a mut self, blocks: &'a [Block], start_block_idx: BlockIdx) -> Self::WriteFuture<'a> { async move { - let card = self.card.as_mut().ok_or(Error::NoCard)?; - let inner = T::inner(); - let state = T::state(); let mut address = start_block_idx.0; for block in blocks.iter() { let block: &[u8; 512] = &block.contents; // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &*(block as *const [u8; 512] as *const [u32; 128]) }; - inner - .write_block(address, buf, card, state, self.config.data_transfer_timeout) - .await?; + let block = unsafe { &*(block as *const _ as *const DataBlock) }; + self.write_block(address, block).await?; address += 1; } Ok(()) From 5e3c33b77766df94f6021457c723ad52d97684b7 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sat, 21 Jan 2023 14:39:25 +0100 Subject: [PATCH 302/374] Fix rcc prescaler for wb55 HCLK1 - fix prescaler not divided which incorrectly set prescaler divided by 3 --- embassy-stm32/src/rcc/wb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index f6a5feea..c9ada83e 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -64,7 +64,7 @@ impl Into for APBPrescaler { impl Into for AHBPrescaler { fn into(self) -> u8 { match self { - AHBPrescaler::NotDivided => 1, + AHBPrescaler::NotDivided => 0x0, AHBPrescaler::Div2 => 0x08, AHBPrescaler::Div3 => 0x01, AHBPrescaler::Div4 => 0x09, From 64e610fef708b7078e9690049583e05e16c6a3bd Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 23 Jan 2023 13:35:24 +0100 Subject: [PATCH 303/374] Replace `Level: Into` with `From` This automatically implements Into for Level --- embassy-nrf/src/gpio.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index b7230302..6ef5033e 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -88,9 +88,9 @@ impl From for Level { } } -impl Into for Level { - fn into(self) -> bool { - match self { +impl From for bool { + fn from(level: Level) -> bool { + match level { Level::Low => false, Level::High => true, } From f38d54a6a6a7fcfb836ea4606f719b2fed877250 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 24 Jan 2023 08:15:22 +0000 Subject: [PATCH 304/374] IPv6 has no checksum --- embassy-net/src/device.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index d0c8a62d..650af23b 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -57,10 +57,6 @@ where ), }; smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); - #[cfg(feature = "proto-ipv6")] - { - smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6); - } smolcaps.checksum.tcp = convert(caps.checksum.tcp); smolcaps.checksum.udp = convert(caps.checksum.udp); smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); From 32bdc54ccba9291b6aafc3c2695a8368d8137bb4 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 24 Jan 2023 08:27:53 +0000 Subject: [PATCH 305/374] Changed crates' names for nrf examples since they were conflicting --- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 994823a9..bbd8a5d2 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-nrf-examples" +name = "embassy-nrf52840-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 03485711..eed49301 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-nrf-examples" +name = "embassy-nrf5340-examples" version = "0.1.0" license = "MIT OR Apache-2.0" From 2a0ea52878ece15fa12c9d70da32b5809aa1e713 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 24 Jan 2023 09:57:26 +0100 Subject: [PATCH 306/374] add missing copy of icmpv6 checksum add proto-ipv6 feature to stm32h7 example to catch issues in CI --- embassy-net/src/device.rs | 4 ++++ examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 650af23b..5daa0054 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -60,6 +60,10 @@ where smolcaps.checksum.tcp = convert(caps.checksum.tcp); smolcaps.checksum.udp = convert(caps.checksum.udp); smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + #[cfg(feature = "proto-ipv6")] + { + smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6); + } smolcaps } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index ff38440a..bcf97641 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,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", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } From 34b67fe1372c535a659590744242cd4ffd52dfb6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Thu, 26 Jan 2023 20:14:53 +0000 Subject: [PATCH 307/374] STD driver needs a reentrant mutex; logic fixed to be reentrancy-safe --- embassy-time/src/driver_std.rs | 67 +++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index fc7fd197..da46a599 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -1,10 +1,12 @@ -use std::cell::UnsafeCell; +use std::cell::{RefCell, UnsafeCell}; use std::mem::MaybeUninit; use std::sync::{Condvar, Mutex, Once}; use std::time::{Duration as StdDuration, Instant as StdInstant}; use std::{mem, ptr, thread}; use atomic_polyfill::{AtomicU8, Ordering}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex as EmbassyMutex; use crate::driver::{AlarmHandle, Driver}; @@ -35,7 +37,10 @@ struct TimeDriver { alarm_count: AtomicU8, once: Once, - alarms: UninitCell>, + // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't + // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections + // themselves are reentrant + alarms: UninitCell>>, zero_instant: UninitCell, signaler: UninitCell, } @@ -53,7 +58,8 @@ crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); + self.alarms + .write(EmbassyMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); self.zero_instant.write(StdInstant::now()); self.signaler.write(Signaler::new()); @@ -66,25 +72,37 @@ impl TimeDriver { loop { let now = DRIVER.now(); - let mut next_alarm = u64::MAX; - { - let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); - for alarm in alarms { - if alarm.timestamp <= now { - alarm.timestamp = u64::MAX; + let next_alarm = unsafe { DRIVER.alarms.as_ref() }.lock(|alarms| { + loop { + let pending = alarms + .borrow_mut() + .iter_mut() + .find(|alarm| alarm.timestamp <= now) + .map(|alarm| { + alarm.timestamp = u64::MAX; - // Call after clearing alarm, so the callback can set another alarm. + (alarm.callback, alarm.ctx) + }); + if let Some((callback, ctx)) = pending { // safety: // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; - f(alarm.ctx); + let f: fn(*mut ()) = unsafe { mem::transmute(callback) }; + f(ctx); } else { - next_alarm = next_alarm.min(alarm.timestamp); + // No alarm due + break; } } - } + + alarms + .borrow() + .iter() + .map(|alarm| alarm.timestamp) + .min() + .unwrap_or(u64::MAX) + }); // Ensure we don't overflow let until = zero @@ -121,18 +139,23 @@ impl Driver for TimeDriver { fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.callback = callback as *const (); - alarm.ctx = ctx; + unsafe { self.alarms.as_ref() }.lock(|alarms| { + let mut alarms = alarms.borrow_mut(); + let alarm = &mut alarms[alarm.id() as usize]; + alarm.callback = callback as *const (); + alarm.ctx = ctx; + }); } fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.timestamp = timestamp; - unsafe { self.signaler.as_ref() }.signal(); + unsafe { self.alarms.as_ref() }.lock(|alarms| { + let mut alarms = alarms.borrow_mut(); + + let alarm = &mut alarms[alarm.id() as usize]; + alarm.timestamp = timestamp; + unsafe { self.signaler.as_ref() }.signal(); + }); true } From 1e60c60afdfc2a4677a3312d7af8d065af6c50ac Mon Sep 17 00:00:00 2001 From: nitroxis Date: Fri, 27 Jan 2023 07:19:34 +0100 Subject: [PATCH 308/374] rp: allow isochronous USB endpoints to be up to 1023 in size --- embassy-rp/src/usb.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 2710c4ad..8eec55b4 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -219,14 +219,16 @@ impl<'d, T: Instance> Driver<'d, T> { let (index, ep) = index.ok_or(EndpointAllocError)?; assert!(!ep.used); - if max_packet_size > 64 { + // as per datasheet, the maximum buffer size is 64, except for isochronous + // endpoints, which are allowed to be up to 1023 bytes. + if (ep_type != EndpointType::Isochronous && max_packet_size > 64) || max_packet_size > 1023 { warn!("max_packet_size too high: {}", max_packet_size); return Err(EndpointAllocError); } // ep mem addrs must be 64-byte aligned, so there's no point in trying // to allocate smaller chunks to save memory. - let len = 64; + let len = (max_packet_size + 63) / 64 * 64; let addr = self.ep_mem_free; if addr + len > EP_MEMORY_SIZE as _ { From c9e2cd6dd41f9281f89ba40cc57dea07b9a224b8 Mon Sep 17 00:00:00 2001 From: nitroxis Date: Fri, 27 Jan 2023 15:49:19 +0100 Subject: [PATCH 309/374] usb: allow adding isochronous endpoints --- embassy-usb/src/builder.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 87a8333b..fc16d2b4 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -401,4 +401,17 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) } + + /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. + /// + /// Descriptors are written in the order builder functions are called. Note that some + /// classes care about the order. + pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { + self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) + } + + /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. + pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { + self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) + } } From e453334870575dd0d18f65dcd207bfbff6e88990 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 27 Jan 2023 15:36:26 +0100 Subject: [PATCH 310/374] LoRa/SX1276: adjust Rx window offset and duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a transmission, two receive windows Rx1 and Rx2 are opened for one second each, one right after the other, after a fixed delay (for example 5s). The Rx window offset is added to the starting date of each window and the Rx window duration represents the maximum delay we will wait for an incoming message before declaring that a timeout occurred. A value of -500ms for the offset and 800ms for the duration means that instead of having Rx1 = [5000, 6000[ and Rx2 = [6000, 7000[ we get Rx1 = [4500, 5300[ and Rx2 = [5500, 6300[. We only cover 30% of the expected windows. The maximum time a SX127x can take before the Rx side is ready is TS_HOP + TS_RE = 50µs + 2.33ms. Using 3ms for the offset and 1003ms for the duration will give much better time windows: Rx1 = [4997, 5997[ and Rx2 = [5997, 7000]. Note that the lorawan-device crate caps Rx1 end date to Rx2 start date. This change allows a previously failing Murata CMWX1ZZABZ-091 module (STM32L + SX1276) to connect to the TTN LoRa network. --- embassy-lora/src/sx127x/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs index f47a9eb5..8904c9a1 100644 --- a/embassy-lora/src/sx127x/mod.rs +++ b/embassy-lora/src/sx127x/mod.rs @@ -70,10 +70,10 @@ where RFS: RadioSwitch + 'static, { fn get_rx_window_offset_ms(&self) -> i32 { - -500 + -3 } fn get_rx_window_duration_ms(&self) -> u32 { - 800 + 1003 } } From 48e1aab762e902ee0a132602d3c2f9ec0551cd6b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 29 Jan 2023 12:55:06 -0600 Subject: [PATCH 311/374] executor: Replace `NonNull` with `TaskRef` --- embassy-executor/src/raw/mod.rs | 63 ++++++++++++++++++------- embassy-executor/src/raw/run_queue.rs | 13 ++--- embassy-executor/src/raw/timer_queue.rs | 35 ++++++-------- embassy-executor/src/raw/waker.rs | 13 +++-- embassy-executor/src/spawner.rs | 9 ++-- 5 files changed, 76 insertions(+), 57 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 181dabe8..10a154a9 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -43,14 +43,11 @@ pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; /// Raw task header for use in task pointers. -/// -/// This is an opaque struct, used for raw pointers to tasks, for use -/// with funtions like [`wake_task`] and [`task_from_waker`]. -pub struct TaskHeader { +pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 - pub(crate) poll_fn: UninitCell)>, // Valid if STATE_SPAWNED + pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + pub(crate) poll_fn: UninitCell, // Valid if STATE_SPAWNED #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Cell, @@ -59,7 +56,7 @@ pub struct TaskHeader { } impl TaskHeader { - pub(crate) const fn new() -> Self { + const fn new() -> Self { Self { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), @@ -74,6 +71,36 @@ impl TaskHeader { } } +/// This is essentially a `&'static TaskStorage` where the type of the future has been erased. +#[derive(Clone, Copy)] +pub struct TaskRef { + ptr: NonNull, +} + +impl TaskRef { + fn new(task: &'static TaskStorage) -> Self { + Self { + ptr: NonNull::from(task).cast(), + } + } + + /// Safety: The pointer must have been obtained with `Task::as_ptr` + pub(crate) unsafe fn from_ptr(ptr: *const TaskHeader) -> Self { + Self { + ptr: NonNull::new_unchecked(ptr as *mut TaskHeader), + } + } + + pub(crate) fn header(self) -> &'static TaskHeader { + unsafe { self.ptr.as_ref() } + } + + /// The returned pointer is valid for the entire TaskStorage. + pub(crate) fn as_ptr(self) -> *const TaskHeader { + self.ptr.as_ptr() + } +} + /// Raw storage in which a task can be spawned. /// /// This struct holds the necessary memory to spawn one task whose future is `F`. @@ -135,14 +162,14 @@ impl TaskStorage { .is_ok() } - unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> NonNull { + unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> TaskRef { // Initialize the task self.raw.poll_fn.write(Self::poll); self.future.write(future()); - NonNull::new_unchecked(self as *const TaskStorage as *const TaskHeader as *mut TaskHeader) + TaskRef::new(self) } - unsafe fn poll(p: NonNull) { + unsafe fn poll(p: TaskRef) { let this = &*(p.as_ptr() as *const TaskStorage); let future = Pin::new_unchecked(this.future.as_mut()); @@ -307,7 +334,7 @@ impl Executor { /// - `task` must be set up to run in this executor. /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] - unsafe fn enqueue(&self, cs: CriticalSection, task: NonNull) { + unsafe fn enqueue(&self, cs: CriticalSection, task: TaskRef) { #[cfg(feature = "rtos-trace")] trace::task_ready_begin(task.as_ptr() as u32); @@ -325,8 +352,8 @@ impl Executor { /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. /// In this case, the task's Future must be Send. This is because this is effectively /// sending the task to the executor thread. - pub(super) unsafe fn spawn(&'static self, task: NonNull) { - task.as_ref().executor.set(self); + pub(super) unsafe fn spawn(&'static self, task: TaskRef) { + task.header().executor.set(self); #[cfg(feature = "rtos-trace")] trace::task_new(task.as_ptr() as u32); @@ -359,7 +386,7 @@ impl Executor { self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); self.run_queue.dequeue_all(|p| { - let task = p.as_ref(); + let task = p.header(); #[cfg(feature = "integrated-timers")] task.expires_at.set(Instant::MAX); @@ -378,7 +405,7 @@ impl Executor { trace::task_exec_begin(p.as_ptr() as u32); // Run the task - task.poll_fn.read()(p as _); + task.poll_fn.read()(p); #[cfg(feature = "rtos-trace")] trace::task_exec_end(); @@ -424,9 +451,9 @@ impl Executor { /// # Safety /// /// `task` must be a valid task pointer obtained from [`task_from_waker`]. -pub unsafe fn wake_task(task: NonNull) { +pub unsafe fn wake_task(task: TaskRef) { critical_section::with(|cs| { - let header = task.as_ref(); + let header = task.header(); let state = header.state.load(Ordering::Relaxed); // If already scheduled, or if not started, @@ -450,7 +477,7 @@ struct TimerQueue; impl embassy_time::queue::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); - let task = unsafe { task.as_ref() }; + let task = task.header(); let expires_at = task.expires_at.get(); task.expires_at.set(expires_at.min(at)); } diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index ed8c82a5..36215753 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -4,7 +4,7 @@ use core::ptr::NonNull; use atomic_polyfill::{AtomicPtr, Ordering}; use critical_section::CriticalSection; -use super::TaskHeader; +use super::{TaskHeader, TaskRef}; pub(crate) struct RunQueueItem { next: AtomicPtr, @@ -46,25 +46,26 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: NonNull) -> bool { + pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: TaskRef) -> bool { let prev = self.head.load(Ordering::Relaxed); - task.as_ref().run_queue_item.next.store(prev, Ordering::Relaxed); - self.head.store(task.as_ptr(), Ordering::Relaxed); + task.header().run_queue_item.next.store(prev, Ordering::Relaxed); + self.head.store(task.as_ptr() as _, Ordering::Relaxed); prev.is_null() } /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - pub(crate) fn dequeue_all(&self, on_task: impl Fn(NonNull)) { + pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { // Atomically empty the queue. let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); // Iterate the linked list of tasks that were previously in the queue. while let Some(task) = NonNull::new(ptr) { + let task = unsafe { TaskRef::from_ptr(task.as_ptr()) }; // If the task re-enqueues itself, the `next` pointer will get overwritten. // Therefore, first read the next pointer, and only then process the task. - let next = unsafe { task.as_ref() }.run_queue_item.next.load(Ordering::Relaxed); + let next = task.header().run_queue_item.next.load(Ordering::Relaxed); on_task(task); diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 24c31892..57d6d3cd 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,45 +1,39 @@ use core::cell::Cell; use core::cmp::min; -use core::ptr; -use core::ptr::NonNull; use atomic_polyfill::Ordering; use embassy_time::Instant; -use super::{TaskHeader, STATE_TIMER_QUEUED}; +use super::{TaskRef, STATE_TIMER_QUEUED}; pub(crate) struct TimerQueueItem { - next: Cell<*mut TaskHeader>, + next: Cell>, } impl TimerQueueItem { pub const fn new() -> Self { - Self { - next: Cell::new(ptr::null_mut()), - } + Self { next: Cell::new(None) } } } pub(crate) struct TimerQueue { - head: Cell<*mut TaskHeader>, + head: Cell>, } impl TimerQueue { pub const fn new() -> Self { - Self { - head: Cell::new(ptr::null_mut()), - } + Self { head: Cell::new(None) } } - pub(crate) unsafe fn update(&self, p: NonNull) { - let task = p.as_ref(); + pub(crate) unsafe fn update(&self, p: TaskRef) { + let task = p.header(); if task.expires_at.get() != Instant::MAX { let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel); let is_new = old_state & STATE_TIMER_QUEUED == 0; if is_new { task.timer_queue_item.next.set(self.head.get()); - self.head.set(p.as_ptr()); + self.head.set(Some(p)); } } } @@ -47,7 +41,7 @@ impl TimerQueue { pub(crate) unsafe fn next_expiration(&self) -> Instant { let mut res = Instant::MAX; self.retain(|p| { - let task = p.as_ref(); + let task = p.header(); let expires = task.expires_at.get(); res = min(res, expires); expires != Instant::MAX @@ -55,9 +49,9 @@ impl TimerQueue { res } - pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(NonNull)) { + pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(TaskRef)) { self.retain(|p| { - let task = p.as_ref(); + let task = p.header(); if task.expires_at.get() <= now { on_task(p); false @@ -67,11 +61,10 @@ impl TimerQueue { }); } - pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull) -> bool) { + pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { let mut prev = &self.head; - while !prev.get().is_null() { - let p = NonNull::new_unchecked(prev.get()); - let task = &*p.as_ptr(); + while let Some(p) = prev.get() { + let task = p.header(); if f(p) { // Skip to next prev = &task.timer_queue_item.next; diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index 5765259f..400b37fa 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -1,8 +1,7 @@ use core::mem; -use core::ptr::NonNull; use core::task::{RawWaker, RawWakerVTable, Waker}; -use super::{wake_task, TaskHeader}; +use super::{wake_task, TaskHeader, TaskRef}; const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); @@ -11,14 +10,14 @@ unsafe fn clone(p: *const ()) -> RawWaker { } unsafe fn wake(p: *const ()) { - wake_task(NonNull::new_unchecked(p as *mut TaskHeader)) + wake_task(TaskRef::from_ptr(p as *const TaskHeader)) } unsafe fn drop(_: *const ()) { // nop } -pub(crate) unsafe fn from_task(p: NonNull) -> Waker { +pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE)) } @@ -33,7 +32,7 @@ pub(crate) unsafe fn from_task(p: NonNull) -> Waker { /// # Panics /// /// Panics if the waker is not created by the Embassy executor. -pub fn task_from_waker(waker: &Waker) -> NonNull { +pub fn task_from_waker(waker: &Waker) -> TaskRef { // safety: OK because WakerHack has the same layout as Waker. // This is not really guaranteed because the structs are `repr(Rust)`, it is // indeed the case in the current implementation. @@ -43,8 +42,8 @@ pub fn task_from_waker(waker: &Waker) -> NonNull { panic!("Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor.") } - // safety: we never create a waker with a null data pointer. - unsafe { NonNull::new_unchecked(hack.data as *mut TaskHeader) } + // safety: our wakers are always created with `TaskRef::as_ptr` + unsafe { TaskRef::from_ptr(hack.data as *const TaskHeader) } } struct WakerHack { diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 400d973f..650ea06c 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,7 +1,6 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::mem; -use core::ptr::NonNull; use core::task::Poll; use super::raw; @@ -22,12 +21,12 @@ use super::raw; /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. #[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] pub struct SpawnToken { - raw_task: Option>, + raw_task: Option, phantom: PhantomData<*mut S>, } impl SpawnToken { - pub(crate) unsafe fn new(raw_task: NonNull) -> Self { + pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self { Self { raw_task: Some(raw_task), phantom: PhantomData, @@ -92,7 +91,7 @@ impl Spawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| unsafe { let task = raw::task_from_waker(cx.waker()); - let executor = (*task.as_ptr()).executor.get(); + let executor = task.header().executor.get(); Poll::Ready(Self::new(&*executor)) }) .await @@ -168,7 +167,7 @@ impl SendSpawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| unsafe { let task = raw::task_from_waker(cx.waker()); - let executor = (*task.as_ptr()).executor.get(); + let executor = task.header().executor.get(); Poll::Ready(Self::new(&*executor)) }) .await From b6ca6d699ad529f56901485fed86bee1ececad6d Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 29 Jan 2023 16:32:12 -0600 Subject: [PATCH 312/374] Make `wake_task` safe --- embassy-executor/src/raw/mod.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 10a154a9..183c5e6a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -444,14 +444,10 @@ impl Executor { } } -/// Wake a task by raw pointer. +/// Wake a task by `TaskRef`. /// -/// You can obtain task pointers from `Waker`s using [`task_from_waker`]. -/// -/// # Safety -/// -/// `task` must be a valid task pointer obtained from [`task_from_waker`]. -pub unsafe fn wake_task(task: TaskRef) { +/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. +pub fn wake_task(task: TaskRef) { critical_section::with(|cs| { let header = task.header(); let state = header.state.load(Ordering::Relaxed); @@ -465,8 +461,10 @@ pub unsafe fn wake_task(task: TaskRef) { header.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); // We have just marked the task as scheduled, so enqueue it. - let executor = &*header.executor.get(); - executor.enqueue(cs, task); + unsafe { + let executor = &*header.executor.get(); + executor.enqueue(cs, task); + } }) } From 768fe699cfbe10016cd4bb93bb78f9d89e1a8e96 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 31 Jan 2023 19:36:37 +0100 Subject: [PATCH 313/374] Pass the correct buffer when creating TcpSocket --- embassy-net/src/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0fbf0c91..d46bd4db 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -389,7 +389,7 @@ pub mod client { fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; Ok(Self { - socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) }, + socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().1, &mut bufs.as_mut().0) }, state, bufs, }) From ca10fe7135d10084e38038f3cd433da39e505bea Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 31 Jan 2023 22:27:19 +0100 Subject: [PATCH 314/374] usb: docs --- embassy-nrf/src/usb.rs | 8 +- embassy-rp/src/usb.rs | 16 +-- embassy-stm32/src/usb/usb.rs | 16 +-- embassy-stm32/src/usb_otg/usb.rs | 16 +-- embassy-usb-driver/README.md | 32 +++++ embassy-usb-driver/src/lib.rs | 116 ++++++++++++------- embassy-usb/README.md | 22 ++++ embassy-usb/src/builder.rs | 24 ++-- embassy-usb/src/class/cdc_acm.rs | 9 ++ embassy-usb/src/class/cdc_ncm/embassy_net.rs | 11 ++ embassy-usb/src/class/cdc_ncm/mod.rs | 51 +++++--- embassy-usb/src/class/hid.rs | 21 ++++ embassy-usb/src/class/mod.rs | 1 + embassy-usb/src/control.rs | 7 ++ embassy-usb/src/descriptor.rs | 4 +- embassy-usb/src/lib.rs | 8 +- embassy-usb/src/types.rs | 2 + 17 files changed, 267 insertions(+), 97 deletions(-) create mode 100644 embassy-usb-driver/README.md create mode 100644 embassy-usb/README.md diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 6be4fec8..f030b909 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -235,7 +235,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> &mut self, ep_type: EndpointType, packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { let index = self.alloc_in.allocate(ep_type)?; let ep_addr = EndpointAddress::from_parts(index, Direction::In); @@ -243,7 +243,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> addr: ep_addr, ep_type, max_packet_size: packet_size, - interval, + interval_ms, })) } @@ -251,7 +251,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> &mut self, ep_type: EndpointType, packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { let index = self.alloc_out.allocate(ep_type)?; let ep_addr = EndpointAddress::from_parts(index, Direction::Out); @@ -259,7 +259,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> addr: ep_addr, ep_type, max_packet_size: packet_size, - interval, + interval_ms, })) } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 8eec55b4..2e3708ef 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -194,13 +194,13 @@ impl<'d, T: Instance> Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result, driver::EndpointAllocError> { trace!( - "allocating type={:?} mps={:?} interval={}, dir={:?}", + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", ep_type, max_packet_size, - interval, + interval_ms, D::dir() ); @@ -281,7 +281,7 @@ impl<'d, T: Instance> Driver<'d, T> { addr: EndpointAddress::from_parts(index, D::dir()), ep_type, max_packet_size, - interval, + interval_ms, }, buf, }) @@ -298,18 +298,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 03e792a2..0355c5f1 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -268,13 +268,13 @@ impl<'d, T: Instance> Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result, driver::EndpointAllocError> { trace!( - "allocating type={:?} mps={:?} interval={}, dir={:?}", + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", ep_type, max_packet_size, - interval, + interval_ms, D::dir() ); @@ -345,7 +345,7 @@ impl<'d, T: Instance> Driver<'d, T> { addr: EndpointAddress::from_parts(index, D::dir()), ep_type, max_packet_size, - interval, + interval_ms, }, buf, }) @@ -362,18 +362,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index fd2f6707..96c574ca 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -217,13 +217,13 @@ impl<'d, T: Instance> Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result, EndpointAllocError> { trace!( - "allocating type={:?} mps={:?} interval={}, dir={:?}", + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", ep_type, max_packet_size, - interval, + interval_ms, D::dir() ); @@ -292,7 +292,7 @@ impl<'d, T: Instance> Driver<'d, T> { addr: EndpointAddress::from_parts(index, D::dir()), ep_type, max_packet_size, - interval, + interval_ms, }, }) } @@ -308,18 +308,18 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-usb-driver/README.md b/embassy-usb-driver/README.md new file mode 100644 index 00000000..012663c3 --- /dev/null +++ b/embassy-usb-driver/README.md @@ -0,0 +1,32 @@ +# embassy-usb-driver + +This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these +traits to add support for using `embassy-usb` for a given chip/platform. + +The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`] +APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used +with the newer `embassy-usb` without needing updates. + +If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate +instead of this one. + +[`embassy-usb`]: https://crates.io/crates/embassy-usb + +## Interoperability + +This crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +This crate requires nightly Rust, due to using "async fn in trait" support. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 64d64735..e8f71a2d 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![feature(async_fn_in_trait)] #![allow(incomplete_features)] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] /// 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 @@ -95,46 +97,65 @@ impl EndpointAddress { } } +/// Infomation for an endpoint. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointInfo { + /// Endpoint's address. pub addr: EndpointAddress, + /// Endpoint's type. pub ep_type: EndpointType, + /// Max packet size, in bytes. pub max_packet_size: u16, - pub interval: u8, + /// Polling interval, in milliseconds. + pub interval_ms: u8, } -/// Driver for a specific USB peripheral. Implement this to add support for a new hardware -/// platform. +/// Main USB driver trait. +/// +/// Implement this to add support for a new hardware platform. pub trait Driver<'a> { + /// Type of the OUT endpoints for this driver. type EndpointOut: EndpointOut + 'a; + /// Type of the IN endpoints for this driver. type EndpointIn: EndpointIn + 'a; + /// Type of the control pipe for this driver. type ControlPipe: ControlPipe + 'a; + /// Type for bus control for this driver. type Bus: Bus + 'a; - /// Allocates an endpoint and specified endpoint parameters. This method is called by the device - /// and class implementations to allocate endpoints, and can only be called before - /// [`start`](Self::start) is called. + /// Allocates an OUT endpoint. + /// + /// This method is called by the USB stack to allocate endpoints. + /// It can only be called before [`start`](Self::start) is called. /// /// # Arguments /// - /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should - /// attempt to return an endpoint with the specified address. If None, the implementation - /// should return the next available one. + /// * `ep_type` - the endpoint's type. /// * `max_packet_size` - Maximum packet size in bytes. - /// * `interval` - Polling interval parameter for interrupt endpoints. + /// * `interval_ms` - Polling interval parameter for interrupt endpoints. fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result; + /// Allocates an IN endpoint. + /// + /// This method is called by the USB stack to allocate endpoints. + /// It can only be called before [`start`](Self::start) is called. + /// + /// # Arguments + /// + /// * `ep_type` - the endpoint's type. + /// * `max_packet_size` - Maximum packet size in bytes. + /// * `interval_ms` - Polling interval parameter for interrupt endpoints. fn alloc_endpoint_in( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result; /// Start operation of the USB device. @@ -146,35 +167,37 @@ pub trait Driver<'a> { /// This consumes the `Driver` instance, so it's no longer possible to allocate more /// endpoints. fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe); - - /// Indicates that `set_device_address` must be called before accepting the corresponding - /// control transfer, not after. - /// - /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6 - const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false; } +/// USB bus trait. +/// +/// This trait provides methods that act on the whole bus. It is kept owned by +/// the main USB task, and used to manage the bus. pub trait Bus { - /// Enables the USB peripheral. Soon after enabling the device will be reset, so - /// there is no need to perform a USB reset in this method. + /// Enable the USB peripheral. async fn enable(&mut self); - /// Disables and powers down the USB peripheral. + /// Disable and powers down the USB peripheral. async fn disable(&mut self); + /// Wait for a bus-related event. + /// + /// This method should asynchronously wait for an event to happen, then + /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event; - /// Enables or disables an endpoint. + /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); - /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it - /// should be prepared to receive data again. Only used during control transfers. + /// Set or clear the STALL condition for an endpoint. + /// + /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); - /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. + /// Get whether the STALL condition is set for an endpoint. fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; - /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the + /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the /// device. /// /// The default implementation just returns `Unsupported`. @@ -187,7 +210,7 @@ pub trait Bus { Err(Unsupported) } - /// Initiates a remote wakeup of the host by the device. + /// Initiate a remote wakeup of the host by the device. /// /// # Errors /// @@ -196,25 +219,27 @@ pub trait Bus { async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } +/// Endpoint trait, common for OUT and IN. pub trait Endpoint { /// Get the endpoint address fn info(&self) -> &EndpointInfo; - /// Waits for the endpoint to be enabled. + /// Wait for the endpoint to be enabled. async fn wait_enabled(&mut self); } +/// OUT Endpoint trait. pub trait EndpointOut: Endpoint { - /// Reads a single packet of data from the endpoint, and returns the actual length of + /// Read a single packet of data from the endpoint, and return the actual length of /// the packet. /// /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. async fn read(&mut self, buf: &mut [u8]) -> Result; } -/// Trait for USB control pipe. +/// USB control pipe trait. /// -/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single +/// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single /// unit, and manages them together to implement the control pipe state machine. /// /// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that @@ -232,6 +257,14 @@ pub trait EndpointOut: Endpoint { /// accept() or reject() /// ``` /// +/// - control out for setting the device address: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// accept_set_address(addr) or reject() +/// ``` +/// /// - control out with len != 0: /// /// ```not_rust @@ -280,26 +313,26 @@ pub trait ControlPipe { /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize; - /// Reads a single setup packet from the endpoint. + /// Read a single setup packet from the endpoint. async fn setup(&mut self) -> [u8; 8]; - /// Reads a DATA OUT packet into `buf` in response to a control write request. + /// Read a DATA OUT packet into `buf` in response to a control write request. /// /// Must be called after `setup()` for requests with `direction` of `Out` /// and `length` greater than zero. async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result; - /// Sends a DATA IN packet with `data` in response to a control read request. + /// Send a DATA IN packet with `data` in response to a control read request. /// /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; - /// Accepts a control request. + /// Accept a control request. /// /// Causes the STATUS packet for the current request to be ACKed. async fn accept(&mut self); - /// Rejects a control request. + /// Reject a control request. /// /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self); @@ -311,8 +344,9 @@ pub trait ControlPipe { async fn accept_set_address(&mut self, addr: u8); } +/// IN Endpoint trait. pub trait EndpointIn: Endpoint { - /// Writes a single packet of data to the endpoint. + /// Write a single packet of data to the endpoint. async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; } @@ -338,18 +372,22 @@ pub enum Event { PowerRemoved, } +/// Allocating an endpoint failed. +/// +/// This can be due to running out of endpoints, or out of endpoint memory, +/// or because the hardware doesn't support the requested combination of features. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointAllocError; +/// Operation is unsupported by the driver. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Operation is unsupported by the driver. pub struct Unsupported; +/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] pub enum EndpointError { /// Either the packet to be written is too long to fit in the transmission /// buffer or the received packet is too long to fit in `buf`. diff --git a/embassy-usb/README.md b/embassy-usb/README.md new file mode 100644 index 00000000..581c3290 --- /dev/null +++ b/embassy-usb/README.md @@ -0,0 +1,22 @@ +# embassy-usb + +TODO crate description/ + +## Interoperability + +This crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +This crate requires nightly Rust, due to using "async fn in trait" support. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index fc16d2b4..48498994 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -349,11 +349,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { self.builder.config_descriptor.write(descriptor_type, descriptor) } - fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn { + fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { let ep = self .builder .driver - .alloc_endpoint_in(ep_type, max_packet_size, interval) + .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) .expect("alloc_endpoint_in failed"); self.builder.config_descriptor.endpoint(ep.info()); @@ -361,11 +361,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { ep } - fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut { + fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { let ep = self .builder .driver - .alloc_endpoint_out(ep_type, max_packet_size, interval) + .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) .expect("alloc_endpoint_out failed"); self.builder.config_descriptor.endpoint(ep.info()); @@ -393,25 +393,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. - pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { - self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval) + pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { + self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms) } /// Allocate a INTERRUPT OUT endpoint and write its descriptor. - pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { - self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) + pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { + self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms) } /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. /// /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. - pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { - self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) + pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { + self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms) } /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. - pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { - self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) + pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { + self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms) } } diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 84db2062..09f17456 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,3 +1,5 @@ +//! CDC-ACM class implementation, aka Serial over USB. + use core::cell::Cell; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; @@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20; const REQ_GET_LINE_CODING: u8 = 0x21; const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; +/// Internal state for CDC-ACM pub struct State<'a> { control: MaybeUninit>, shared: ControlShared, } impl<'a> State<'a> { + /// Create a new `State`. pub fn new() -> Self { Self { control: MaybeUninit::uninit(), @@ -284,10 +288,15 @@ impl From for StopBits { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParityType { + /// No parity bit. None = 0, + /// Parity bit is 1 if the amount of `1` bits in the data byte is odd. Odd = 1, + /// Parity bit is 1 if the amount of `1` bits in the data byte is even. Even = 2, + /// Parity bit is always 1 Mark = 3, + /// Parity bit is always 0 Space = 4, } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 501df2d8..bc79b367 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,3 +1,4 @@ +//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class. use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; @@ -5,11 +6,13 @@ use embassy_usb_driver::Driver; use super::{CdcNcmClass, Receiver, Sender}; +/// Internal state for the embassy-net integration. pub struct State { ch_state: ch::State, } impl State { + /// Create a new `State`. pub const fn new() -> Self { Self { ch_state: ch::State::new(), @@ -17,6 +20,9 @@ impl State, const MTU: usize> { tx_usb: Sender<'d, D>, rx_usb: Receiver<'d, D>, @@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { } impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { + /// Run the CDC-NCM class. + /// + /// You must call this in a background task for the class to operate. pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { @@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { // would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? //pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; +/// Type alias for the embassy-net driver for CDC-NCM. pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { + /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net). pub fn into_embassy_net_device( self, state: &'d mut State, diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 4954a65b..5e59b72f 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -1,18 +1,19 @@ -/// CDC-NCM, aka Ethernet over USB. -/// -/// # Compatibility -/// -/// Windows: NOT supported in Windows 10. Supported in Windows 11. -/// -/// Linux: Well-supported since forever. -/// -/// Android: Support for CDC-NCM is spotty and varies across manufacturers. -/// -/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. -/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), -/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. -/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 -/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 +//! CDC-NCM class implementation, aka Ethernet over USB. +//! +//! # Compatibility +//! +//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11. +//! +//! Linux: Well-supported since forever. +//! +//! Android: Support for CDC-NCM is spotty and varies across manufacturers. +//! +//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12. +//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), +//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. +//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 +//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 + use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; @@ -114,6 +115,7 @@ fn byteify(buf: &mut [u8], data: T) -> &[u8] { &buf[..len] } +/// Internal state for the CDC-NCM class. pub struct State<'a> { comm_control: MaybeUninit>, data_control: MaybeUninit, @@ -121,6 +123,7 @@ pub struct State<'a> { } impl<'a> State<'a> { + /// Create a new `State`. pub fn new() -> Self { Self { comm_control: MaybeUninit::uninit(), @@ -223,6 +226,7 @@ impl ControlHandler for DataControl { } } +/// CDC-NCM class pub struct CdcNcmClass<'d, D: Driver<'d>> { _comm_if: InterfaceNumber, comm_ep: D::EndpointIn, @@ -235,6 +239,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> { } impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { + /// Create a new CDC NCM class. pub fn new( builder: &mut Builder<'d, D>, state: &'d mut State<'d>, @@ -319,6 +324,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { } } + /// Split the class into a sender and receiver. + /// + /// This allows concurrently sending and receiving packets from separate tasks. pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { ( Sender { @@ -334,12 +342,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { } } +/// CDC NCM class packet sender. +/// +/// You can obtain a `Sender` with [`CdcNcmClass::split`] pub struct Sender<'d, D: Driver<'d>> { write_ep: D::EndpointIn, seq: u16, } impl<'d, D: Driver<'d>> Sender<'d, D> { + /// Write a packet. + /// + /// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers. pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { let seq = self.seq; self.seq = self.seq.wrapping_add(1); @@ -393,6 +407,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { } } +/// CDC NCM class packet receiver. +/// +/// You can obtain a `Receiver` with [`CdcNcmClass::split`] pub struct Receiver<'d, D: Driver<'d>> { data_if: InterfaceNumber, comm_ep: D::EndpointIn, @@ -400,7 +417,9 @@ pub struct Receiver<'d, D: Driver<'d>> { } impl<'d, D: Driver<'d>> Receiver<'d, D> { - /// Reads a single packet from the OUT endpoint. + /// Write a network packet. + /// + /// This waits until a packet is succesfully received from the endpoint buffers. pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result { // Retry loop loop { diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index b967aba0..2493061b 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -1,3 +1,5 @@ +//! USB HID (Human Interface Device) class implementation. + use core::mem::MaybeUninit; use core::ops::Range; use core::sync::atomic::{AtomicUsize, Ordering}; @@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09; const HID_REQ_GET_PROTOCOL: u8 = 0x03; const HID_REQ_SET_PROTOCOL: u8 = 0x0b; +/// Configuration for the HID class. pub struct Config<'d> { /// HID report descriptor. pub report_descriptor: &'d [u8], @@ -46,11 +49,15 @@ pub struct Config<'d> { pub max_packet_size: u16, } +/// Report ID #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ReportId { + /// IN report In(u8), + /// OUT report Out(u8), + /// Feature report Feature(u8), } @@ -65,12 +72,14 @@ impl ReportId { } } +/// Internal state for USB HID. pub struct State<'d> { control: MaybeUninit>, out_report_offset: AtomicUsize, } impl<'d> State<'d> { + /// Create a new `State`. pub fn new() -> Self { State { control: MaybeUninit::uninit(), @@ -79,6 +88,7 @@ impl<'d> State<'d> { } } +/// USB HID reader/writer. pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { reader: HidReader<'d, D, READ_N>, writer: HidWriter<'d, D, WRITE_N>, @@ -180,20 +190,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit } } +/// USB HID writer. +/// +/// You can obtain a `HidWriter` using [`HidReaderWriter::split`]. pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { ep_in: D::EndpointIn, } +/// USB HID reader. +/// +/// You can obtain a `HidReader` using [`HidReaderWriter::split`]. pub struct HidReader<'d, D: Driver<'d>, const N: usize> { ep_out: D::EndpointOut, offset: &'d AtomicUsize, } +/// Error when reading a HID report. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ReadError { + /// The given buffer was too small to read the received report. BufferOverflow, + /// The endpoint is disabled. Disabled, + /// The report was only partially read. See [`HidReader::read`] for details. Sync(Range), } @@ -344,6 +364,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> { } } +/// Handler for HID-related control requests. pub trait RequestHandler { /// Reads the value of report `id` into `buf` returning the size. /// diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs index af27577a..b23e03d4 100644 --- a/embassy-usb/src/class/mod.rs +++ b/embassy-usb/src/class/mod.rs @@ -1,3 +1,4 @@ +//! Implementations of well-known USB classes. pub mod cdc_acm; pub mod cdc_ncm; pub mod hid; diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index d6d0c656..39b499f0 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -126,17 +126,23 @@ impl Request { } } +/// Response for a CONTROL OUT request. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OutResponse { + /// The request was accepted. Accepted, + /// The request was rejected. Rejected, } +/// Response for a CONTROL IN request. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum InResponse<'a> { + /// The request was accepted. The buffer contains the response data. Accepted(&'a [u8]), + /// The request was rejected. Rejected, } @@ -148,6 +154,7 @@ pub trait ControlHandler { /// Called after a USB reset after the bus reset sequence is complete. fn reset(&mut self) {} + /// Called when a "set alternate setting" control request is done on the interface. fn set_alternate_setting(&mut self, alternate_setting: u8) { let _ = alternate_setting; } diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index 497f0319..ae38e26c 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,3 +1,5 @@ +//! Utilities for writing USB descriptors. + use crate::builder::Config; use crate::driver::EndpointInfo; use crate::types::*; @@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> { endpoint.ep_type as u8, // bmAttributes endpoint.max_packet_size as u8, (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize - endpoint.interval, // bInterval + endpoint.interval_ms, // bInterval ], ); } diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 096e8b07..2656af29 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] #![feature(type_alias_impl_trait)] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; @@ -46,10 +48,13 @@ pub enum UsbDeviceState { Configured, } +/// Error returned by [`UsbDevice::remote_wakeup`]. #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RemoteWakeupError { + /// The USB device is not suspended, or remote wakeup was not enabled. InvalidState, + /// The underlying driver doesn't support remote wakeup. Unsupported, } @@ -65,6 +70,7 @@ pub const CONFIGURATION_NONE: u8 = 0; /// The bConfiguration value for the single configuration supported by this device. pub const CONFIGURATION_VALUE: u8 = 1; +/// Maximum interface count, configured at compile time. pub const MAX_INTERFACE_COUNT: usize = 4; const STRING_INDEX_MANUFACTURER: u8 = 1; @@ -100,6 +106,7 @@ struct Interface<'d> { num_strings: u8, } +/// Main struct for the USB device stack. pub struct UsbDevice<'d, D: Driver<'d>> { control_buf: &'d mut [u8], control: D::ControlPipe, @@ -489,7 +496,6 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { .unwrap(); // TODO check it is valid (not out of range) - // TODO actually enable/disable endpoints. if let Some(handler) = &mut iface.handler { handler.set_alternate_setting(new_altsetting); diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index aeab063d..1743e61f 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,3 +1,5 @@ +//! USB types. + /// A handle for a USB interface that contains its number. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From b5cf332cc076a0de11ce6a0563a2235c9e57eb5c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Feb 2023 00:48:33 +0100 Subject: [PATCH 315/374] nrf: docs. --- embassy-hal-common/src/macros.rs | 6 + .../src/macros/cortex_m_interrupt_declare.rs | 3 + embassy-nrf/README.md | 58 ++++++++++ embassy-nrf/src/buffered_uarte.rs | 9 +- embassy-nrf/src/chips/nrf5340_app.rs | 1 + embassy-nrf/src/chips/nrf5340_net.rs | 1 + embassy-nrf/src/chips/nrf9160.rs | 1 + embassy-nrf/src/gpio.rs | 2 +- embassy-nrf/src/gpiote.rs | 53 +++++++-- embassy-nrf/src/i2s.rs | 109 +++++++++++++----- embassy-nrf/src/lib.rs | 46 +------- embassy-nrf/src/nvmc.rs | 2 +- embassy-nrf/src/pdm.rs | 14 ++- embassy-nrf/src/ppi/dppi.rs | 3 + embassy-nrf/src/ppi/mod.rs | 2 +- embassy-nrf/src/ppi/ppi.rs | 2 +- embassy-nrf/src/pwm.rs | 22 +++- embassy-nrf/src/qdec.rs | 37 +++++- embassy-nrf/src/qspi.rs | 42 +++++++ embassy-nrf/src/rng.rs | 6 +- embassy-nrf/src/saadc.rs | 56 +++++---- embassy-nrf/src/spim.rs | 33 ++++-- embassy-nrf/src/spis.rs | 36 ++++-- embassy-nrf/src/temp.rs | 5 +- embassy-nrf/src/timer.rs | 36 +++++- embassy-nrf/src/twim.rs | 73 +++++++++--- embassy-nrf/src/twis.rs | 57 +++++++-- embassy-nrf/src/uarte.rs | 76 ++++++++---- embassy-nrf/src/usb.rs | 90 ++++++++++----- embassy-nrf/src/wdt.rs | 8 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf52840/src/bin/i2s_effect.rs | 6 +- examples/nrf52840/src/bin/i2s_monitor.rs | 6 +- examples/nrf52840/src/bin/i2s_waveform.rs | 6 +- examples/nrf52840/src/bin/saadc_continuous.rs | 4 +- examples/nrf52840/src/bin/usb_ethernet.rs | 6 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 4 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 4 +- examples/nrf52840/src/bin/usb_serial.rs | 6 +- .../nrf52840/src/bin/usb_serial_multitask.rs | 6 +- 40 files changed, 694 insertions(+), 245 deletions(-) create mode 100644 embassy-nrf/README.md diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs index 7af85f78..da791313 100644 --- a/embassy-hal-common/src/macros.rs +++ b/embassy-hal-common/src/macros.rs @@ -1,10 +1,12 @@ #[macro_export] macro_rules! peripherals { ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + /// Types for the peripheral singletons. pub mod peripherals { $( $(#[$cfg])? #[allow(non_camel_case_types)] + #[doc = concat!(stringify!($name), " peripheral")] pub struct $name { _private: () } $(#[$cfg])? @@ -25,9 +27,13 @@ macro_rules! peripherals { )* } + /// Struct containing all the peripheral singletons. + /// + /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]. #[allow(non_snake_case)] pub struct Peripherals { $( + #[doc = concat!(stringify!($name), " peripheral")] $(#[$cfg])? pub $name: peripherals::$name, )* diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs index ab61ad5d..ebbb47cd 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs @@ -6,7 +6,10 @@ pub fn run(name: syn::Ident) -> Result { let name_interrupt = format_ident!("{}", name); let name_handler = format!("__EMBASSY_{}_HANDLER", name); + let doc = format!("{} interrupt singleton.", name); + let result = quote! { + #[doc = #doc] #[allow(non_camel_case_types)] pub struct #name_interrupt(()); unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name_interrupt { diff --git a/embassy-nrf/README.md b/embassy-nrf/README.md new file mode 100644 index 00000000..a31cfae6 --- /dev/null +++ b/embassy-nrf/README.md @@ -0,0 +1,58 @@ +# Embassy nRF HAL + +HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. + +The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs +for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to +complete operations in low power mod and handling interrupts, so that applications can focus on more important matters. + +## EasyDMA considerations + +On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting +with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). +However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust +slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: + +```no_run +// As we pass a slice to the function whose contents will not ever change, +// the compiler writes it into the flash and thus the pointer to it will +// reference static memory. Since EasyDMA requires slices to reside in RAM, +// this function call will fail. +let result = spim.write_from_ram(&[1, 2, 3]); +assert_eq!(result, Err(Error::BufferNotInRAM)); + +// The data is still static and located in flash. However, since we are assigning +// it to a variable, the compiler will load it into memory. Passing a reference to the +// variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. +// This function call succeeds. +let data = [1, 2, 3]; +let result = spim.write_from_ram(&data); +assert!(result.is_ok()); +``` + +Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: +- Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM. +- Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission. + +Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will +fail and notify you, or the more convenient versions without the suffix which are potentially a little bit +more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage +as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). + +Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as +mutable slices always reside in RAM. + +## Minimum supported Rust version (MSRV) + +Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index ea25236f..112f084c 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -1,4 +1,4 @@ -//! Async buffered UART +//! Async buffered UART driver. //! //! WARNING!!! The functionality provided here is intended to be used only //! in situations where hardware flow control are available i.e. CTS and RTS. @@ -69,7 +69,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> { tx_waker: WakerRegistration, } -/// Interface to a UARTE instance +/// Buffered UARTE driver. pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { inner: RefCell>>, } @@ -199,6 +199,9 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { }); } + /// Split the UART in reader and writer parts. + /// + /// This allows reading and writing concurrently from independent tasks. pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) { (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) } @@ -320,10 +323,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } } +/// Reader part of the buffered UARTE driver. pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } +/// Writer part of the buffered UARTE driver. pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 4575f09f..c600fcbf 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -1,3 +1,4 @@ +/// Peripheral Access Crate #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 54827238..a46583ec 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -1,3 +1,4 @@ +/// Peripheral Access Crate #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 472ee677..e1509ddd 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -1,3 +1,4 @@ +/// Peripheral Access Crate #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 6ef5033e..895ab934 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -1,4 +1,4 @@ -//! General purpose input/output for nRF. +//! General purpose input/output (GPIO) driver. #![macro_use] use core::convert::Infallible; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 7467dbc6..e1816eb9 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,3 +1,5 @@ +//! GPIO task/event (GPIOTE) driver. + use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; @@ -11,29 +13,38 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::ppi::{Event, Task}; use crate::{interrupt, pac, peripherals}; -pub const CHANNEL_COUNT: usize = 8; +/// Amount of GPIOTE channels in the chip. +const CHANNEL_COUNT: usize = 8; #[cfg(any(feature = "nrf52833", feature = "nrf52840"))] -pub const PIN_COUNT: usize = 48; +const PIN_COUNT: usize = 48; #[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))] -pub const PIN_COUNT: usize = 32; +const PIN_COUNT: usize = 32; #[allow(clippy::declare_interior_mutable_const)] const NEW_AW: AtomicWaker = AtomicWaker::new(); static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; +/// Polarity for listening to events for GPIOTE input channels. pub enum InputChannelPolarity { + /// Don't listen for any pin changes. None, + /// Listen for high to low changes. HiToLo, + /// Listen for low to high changes. LoToHi, + /// Listen for any change, either low to high or high to low. Toggle, } -/// Polarity of the `task out` operation. +/// Polarity of the OUT task operation for GPIOTE output channels. pub enum OutputChannelPolarity { + /// Set the pin high. Set, + /// Set the pin low. Clear, + /// Toggle the pin. Toggle, } @@ -162,6 +173,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { } impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { + /// Create a new GPIOTE input channel driver. pub fn new(ch: impl Peripheral

+ 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { into_ref!(ch); @@ -188,6 +200,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { InputChannel { ch, pin } } + /// Asynchronously wait for an event in this channel. pub async fn wait(&self) { let g = regs(); let num = self.ch.number(); @@ -231,6 +244,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { } impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { + /// Create a new GPIOTE output channel driver. pub fn new(ch: impl Peripheral

+ 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { into_ref!(ch); let g = regs(); @@ -258,20 +272,20 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { OutputChannel { ch, _pin: pin } } - /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). + /// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); } - /// Triggers `task set` (set associated pin high). + /// Triggers the SET task (set associated pin high). #[cfg(not(feature = "nrf51"))] pub fn set(&self) { let g = regs(); g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); } - /// Triggers `task clear` (set associated pin low). + /// Triggers the CLEAR task (set associated pin low). #[cfg(not(feature = "nrf51"))] pub fn clear(&self) { let g = regs(); @@ -336,48 +350,58 @@ impl<'a> Future for PortInputFuture<'a> { } impl<'d, T: GpioPin> Input<'d, T> { + /// Wait until the pin is high. If it is already high, return immediately. pub async fn wait_for_high(&mut self) { self.pin.wait_for_high().await } + /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { self.pin.wait_for_low().await } + /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { self.pin.wait_for_rising_edge().await } + /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { self.pin.wait_for_falling_edge().await } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await } } impl<'d, T: GpioPin> Flex<'d, T> { + /// Wait until the pin is high. If it is already high, return immediately. pub async fn wait_for_high(&mut self) { self.pin.conf().modify(|_, w| w.sense().high()); PortInputFuture::new(&mut self.pin).await } + /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { self.pin.conf().modify(|_, w| w.sense().low()); PortInputFuture::new(&mut self.pin).await } + /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { self.wait_for_low().await; self.wait_for_high().await; } + /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { self.wait_for_high().await; self.wait_for_low().await; } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. pub async fn wait_for_any_edge(&mut self) { if self.is_high() { self.pin.conf().modify(|_, w| w.sense().low()); @@ -394,8 +418,17 @@ mod sealed { pub trait Channel {} } +/// GPIOTE channel trait. +/// +/// Implemented by all GPIOTE channels. pub trait Channel: sealed::Channel + Sized { + /// Get the channel number. fn number(&self) -> usize; + + /// Convert this channel to a type-erased `AnyChannel`. + /// + /// This allows using several channels in situations that might require + /// them to be the same type, like putting them in an array. fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() as u8, @@ -403,6 +436,12 @@ pub trait Channel: sealed::Channel + Sized { } } +/// Type-erased channel. +/// +/// Obtained by calling `Channel::degrade`. +/// +/// This allows using several channels in situations that might require +/// them to be the same type, like putting them in an array. pub struct AnyChannel { number: u8, } diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 7e950775..770df7c8 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -1,6 +1,6 @@ -#![macro_use] +//! Inter-IC Sound (I2S) driver. -//! Support for I2S audio +#![macro_use] use core::future::poll_fn; use core::marker::PhantomData; @@ -19,16 +19,23 @@ use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; +/// Type alias for `MultiBuffering` with 2 buffers. pub type DoubleBuffering = MultiBuffering; +/// I2S transfer error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// The buffer is too long. BufferTooLong, + /// The buffer is empty. BufferZeroLength, - BufferNotInDataMemory, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// The buffer address is not aligned. BufferMisaligned, + /// The buffer length is not a multiple of the alignment. BufferLengthMisaligned, } @@ -36,34 +43,16 @@ pub enum Error { #[derive(Clone)] #[non_exhaustive] pub struct Config { + /// Sample width pub sample_width: SampleWidth, + /// Alignment pub align: Align, + /// Sample format pub format: Format, + /// Channel configuration. pub channels: Channels, } -impl Config { - pub fn sample_width(mut self, sample_width: SampleWidth) -> Self { - self.sample_width = sample_width; - self - } - - pub fn align(mut self, align: Align) -> Self { - self.align = align; - self - } - - pub fn format(mut self, format: Format) -> Self { - self.format = format; - self - } - - pub fn channels(mut self, channels: Channels) -> Self { - self.channels = channels; - self - } -} - impl Default for Config { fn default() -> Self { Self { @@ -75,7 +64,7 @@ impl Default for Config { } } -/// I2S Mode +/// I2S clock configuration. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct MasterClock { freq: MckFreq, @@ -83,12 +72,14 @@ pub struct MasterClock { } impl MasterClock { + /// Create a new `MasterClock`. pub fn new(freq: MckFreq, ratio: Ratio) -> Self { Self { freq, ratio } } } impl MasterClock { + /// Get the sample rate for this clock configuration. pub fn sample_rate(&self) -> u32 { self.freq.to_frequency() / self.ratio.to_divisor() } @@ -97,18 +88,31 @@ impl MasterClock { /// Master clock generator frequency. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum MckFreq { + /// 32 Mhz / 8 = 4000.00 kHz _32MDiv8, + /// 32 Mhz / 10 = 3200.00 kHz _32MDiv10, + /// 32 Mhz / 11 = 2909.09 kHz _32MDiv11, + /// 32 Mhz / 15 = 2133.33 kHz _32MDiv15, + /// 32 Mhz / 16 = 2000.00 kHz _32MDiv16, + /// 32 Mhz / 21 = 1523.81 kHz _32MDiv21, + /// 32 Mhz / 23 = 1391.30 kHz _32MDiv23, + /// 32 Mhz / 30 = 1066.67 kHz _32MDiv30, + /// 32 Mhz / 31 = 1032.26 kHz _32MDiv31, + /// 32 Mhz / 32 = 1000.00 kHz _32MDiv32, + /// 32 Mhz / 42 = 761.90 kHz _32MDiv42, + /// 32 Mhz / 63 = 507.94 kHz _32MDiv63, + /// 32 Mhz / 125 = 256.00 kHz _32MDiv125, } @@ -146,14 +150,23 @@ impl From for usize { /// #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Ratio { + /// Divide by 32 _32x, + /// Divide by 48 _48x, + /// Divide by 64 _64x, + /// Divide by 96 _96x, + /// Divide by 128 _128x, + /// Divide by 192 _192x, + /// Divide by 256 _256x, + /// Divide by 384 _384x, + /// Divide by 512 _512x, } @@ -165,6 +178,7 @@ impl Ratio { usize::from(*self) as u8 } + /// Return the divisor for this ratio pub fn to_divisor(&self) -> u32 { Self::RATIOS[usize::from(*self)] } @@ -183,11 +197,17 @@ impl From for usize { /// For custom master clock configuration, please refer to [MasterClock]. #[derive(Clone, Copy)] pub enum ApproxSampleRate { + /// 11025 Hz _11025, + /// 16000 Hz _16000, + /// 22050 Hz _22050, + /// 32000 Hz _32000, + /// 44100 Hz _44100, + /// 48000 Hz _48000, } @@ -211,6 +231,7 @@ impl From for MasterClock { } impl ApproxSampleRate { + /// Get the sample rate as an integer. pub fn sample_rate(&self) -> u32 { MasterClock::from(*self).sample_rate() } @@ -223,20 +244,32 @@ impl ApproxSampleRate { /// For custom master clock configuration, please refer to [Mode]. #[derive(Clone, Copy)] pub enum ExactSampleRate { + /// 8000 Hz _8000, + /// 10582 Hz _10582, + /// 12500 Hz _12500, + /// 15625 Hz _15625, + /// 15873 Hz _15873, + /// 25000 Hz _25000, + /// 31250 Hz _31250, + /// 50000 Hz _50000, + /// 62500 Hz _62500, + /// 100000 Hz _100000, + /// 125000 Hz _125000, } impl ExactSampleRate { + /// Get the sample rate as an integer. pub fn sample_rate(&self) -> u32 { MasterClock::from(*self).sample_rate() } @@ -263,8 +296,11 @@ impl From for MasterClock { /// Sample width. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SampleWidth { + /// 8 bit samples. _8bit, + /// 16 bit samples. _16bit, + /// 24 bit samples. _24bit, } @@ -277,7 +313,9 @@ impl From for u8 { /// Channel used for the most significant sample value in a frame. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Align { + /// Left-align samples. Left, + /// Right-align samples. Right, } @@ -293,7 +331,9 @@ impl From for bool { /// Frame format. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Format { + /// I2S frame format I2S, + /// Aligned frame format Aligned, } @@ -309,8 +349,11 @@ impl From for bool { /// Channels #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Channels { + /// Stereo (2 channels). Stereo, + /// Mono, left channel only. MonoLeft, + /// Mono, right channel only. MonoRight, } @@ -320,7 +363,7 @@ impl From for u8 { } } -/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. +/// I2S driver. pub struct I2S<'d, T: Instance> { i2s: PeripheralRef<'d, T>, irq: PeripheralRef<'d, T::Interrupt>, @@ -566,7 +609,7 @@ impl<'d, T: Instance> I2S<'d, T> { { trace!("SEND: {}", buffer_ptr as *const S as u32); - slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; + slice_in_ram_or(buffer_ptr, Error::BufferNotInRAM)?; compiler_fence(Ordering::SeqCst); @@ -1003,7 +1046,10 @@ impl Device { /// Sample details pub trait Sample: Sized + Copy + Default { + /// Width of this sample type. const WIDTH: usize; + + /// Scale of this sample. const SCALE: Self; } @@ -1022,12 +1068,13 @@ impl Sample for i32 { const SCALE: Self = 1 << (Self::WIDTH - 1); } -/// A 4-bytes aligned buffer. +/// A 4-bytes aligned buffer. Needed for DMA access. #[derive(Clone, Copy)] #[repr(align(4))] pub struct AlignedBuffer([T; N]); impl AlignedBuffer { + /// Create a new `AlignedBuffer`. pub fn new(array: [T; N]) -> Self { Self(array) } @@ -1052,12 +1099,14 @@ impl DerefMut for AlignedBuffer { } } +/// Set of multiple buffers, for multi-buffering transfers. pub struct MultiBuffering { buffers: [AlignedBuffer; NB], index: usize, } impl MultiBuffering { + /// Create a new `MultiBuffering`. pub fn new() -> Self { assert!(NB > 1); Self { @@ -1119,7 +1168,9 @@ pub(crate) mod sealed { } } +/// I2S peripehral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 1dd0e790..20e70a24 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,53 +1,11 @@ -//! # Embassy nRF HAL -//! -//! HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. -//! -//! The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs -//! for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to -//! complete operations in low power mod and handling interrupts, so that applications can focus on more important matters. -//! -//! ## EasyDMA considerations -//! -//! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting -//! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). -//! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust -//! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: -//! -//! ```no_run -//! // As we pass a slice to the function whose contents will not ever change, -//! // the compiler writes it into the flash and thus the pointer to it will -//! // reference static memory. Since EasyDMA requires slices to reside in RAM, -//! // this function call will fail. -//! let result = spim.write_from_ram(&[1, 2, 3]); -//! assert_eq!(result, Err(Error::DMABufferNotInDataMemory)); -//! -//! // The data is still static and located in flash. However, since we are assigning -//! // it to a variable, the compiler will load it into memory. Passing a reference to the -//! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. -//! // This function call succeeds. -//! let data = [1, 2, 3]; -//! let result = spim.write_from_ram(&data); -//! assert!(result.is_ok()); -//! ``` -//! -//! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: -//! - Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM. -//! - Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission. -//! -//! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will -//! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit -//! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage -//! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). -//! -//! Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as -//! mutable slices always reside in RAM. - #![no_std] #![cfg_attr( feature = "nightly", feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 405ea317..c1ffa31a 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -1,4 +1,4 @@ -//! Non-Volatile Memory Controller (NVMC) module. +//! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver. use core::{ptr, slice}; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index b7c7022c..54feca4c 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,4 +1,4 @@ -//! PDM mirophone interface +//! Pulse Density Modulation (PDM) mirophone driver. use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; @@ -22,12 +22,16 @@ pub struct Pdm<'d> { phantom: PhantomData<&'d PDM>, } +/// PDM error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// Buffer is too long. BufferTooLong, + /// Buffer is empty BufferZeroLength, + /// PDM is not running NotRunning, } @@ -119,6 +123,7 @@ impl<'d> Pdm<'d> { r.events_started.reset(); } + /// Sample data into the given buffer. pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { if buffer.len() == 0 { return Err(Error::BufferZeroLength); @@ -215,14 +220,21 @@ impl Default for Config { } } +/// PDM operation mode. #[derive(PartialEq)] pub enum OperationMode { + /// Mono (1 channel) Mono, + /// Stereo (2 channels) Stereo, } + +/// PDM edge polarity #[derive(PartialEq)] pub enum Edge { + /// Left edge is rising LeftRising, + /// Left edge is falling LeftFalling, } diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index de856c0c..0908cd7b 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -11,12 +11,14 @@ fn regs() -> &'static pac::dppic::RegisterBlock { } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { + /// Configure PPI channel to trigger `task` on `event`. pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event, task: Task) -> Self { Ppi::new_many_to_many(ch, [event], [task]) } } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { + /// Configure PPI channel to trigger both `task1` and `task2` on `event`. pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { Ppi::new_many_to_many(ch, [event], [task1, task2]) } @@ -25,6 +27,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { + /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires. pub fn new_many_to_many( ch: impl Peripheral

+ 'd, events: [Event; EVENT_COUNT], diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 8f5ed14c..b76eccf0 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -1,6 +1,6 @@ #![macro_use] -//! HAL interface for the PPI and DPPI peripheral. +//! Programmable Peripheral Interconnect (PPI/DPPI) driver. //! //! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability //! between peripherals through their events and tasks. There are fixed PPI channels and fully diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 19abc4e1..a96ab50b 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -48,7 +48,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { #[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { - /// Configure PPI channel to trigger `task1` and `task2` on `event`. + /// Configure PPI channel to trigger both `task1` and `task2` on `event`. pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { into_ref!(ch); diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 5f750a91..708f2310 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -1,3 +1,5 @@ +//! Pulse Width Modulation (PWM) driver. + #![macro_use] use core::sync::atomic::{compiler_fence, Ordering}; @@ -32,6 +34,7 @@ pub struct SequencePwm<'d, T: Instance> { ch3: Option>, } +/// PWM error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -41,7 +44,7 @@ pub enum Error { /// Min Sequence count is 1 SequenceTimesAtLeastOne, /// EasyDMA can only read from data memory, read only buffers in flash will fail. - DMABufferNotInDataMemory, + BufferNotInRAM, } const MAX_SEQUENCE_LEN: usize = 32767; @@ -358,6 +361,7 @@ pub struct Sequence<'s> { } impl<'s> Sequence<'s> { + /// Create a new `Sequence` pub fn new(words: &'s [u16], config: SequenceConfig) -> Self { Self { words, config } } @@ -367,7 +371,7 @@ impl<'s> Sequence<'s> { /// Takes at one sequence along with its configuration. #[non_exhaustive] pub struct SingleSequencer<'d, 's, T: Instance> { - pub sequencer: Sequencer<'d, 's, T>, + sequencer: Sequencer<'d, 's, T>, } impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { @@ -428,8 +432,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { let sequence0 = &self.sequence0; let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0); - slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?; - slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?; + slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?; if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { return Err(Error::SequenceTooLong); @@ -536,13 +540,21 @@ pub enum SequenceMode { /// PWM Base clock is system clock (16MHz) divided by prescaler #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Prescaler { + /// Divide by 1 Div1, + /// Divide by 2 Div2, + /// Divide by 4 Div4, + /// Divide by 8 Div8, + /// Divide by 16 Div16, + /// Divide by 32 Div32, + /// Divide by 64 Div64, + /// Divide by 128 Div128, } @@ -828,7 +840,9 @@ pub(crate) mod sealed { } } +/// PWM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 253c85c3..c01babca 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,4 +1,4 @@ -//! Quadrature decoder interface +//! Quadrature decoder (QDEC) driver. use core::future::poll_fn; use core::task::Poll; @@ -12,17 +12,23 @@ use crate::interrupt::InterruptExt; use crate::peripherals::QDEC; use crate::{interrupt, pac, Peripheral}; -/// Quadrature decoder +/// Quadrature decoder driver. pub struct Qdec<'d> { _p: PeripheralRef<'d, QDEC>, } +/// QDEC config #[non_exhaustive] pub struct Config { + /// Number of samples pub num_samples: NumSamples, + /// Sample period pub period: SamplePeriod, + /// Set LED output pin polarity pub led_polarity: LedPolarity, + /// Enable/disable input debounce filters pub debounce: bool, + /// Time period the LED is switched ON prior to sampling (0..511 us). pub led_pre_usecs: u16, } @@ -41,6 +47,7 @@ impl Default for Config { static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Qdec<'d> { + /// Create a new QDEC. pub fn new( qdec: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -52,6 +59,7 @@ impl<'d> Qdec<'d> { Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) } + /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( qdec: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -170,36 +178,61 @@ impl<'d> Qdec<'d> { } } +/// Sample period #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SamplePeriod { + /// 128 us _128us, + /// 256 us _256us, + /// 512 us _512us, + /// 1024 us _1024us, + /// 2048 us _2048us, + /// 4096 us _4096us, + /// 8192 us _8192us, + /// 16384 us _16384us, + /// 32 ms _32ms, + /// 65 ms _65ms, + /// 131 ms _131ms, } +/// Number of samples taken. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum NumSamples { + /// 10 samples _10smpl, + /// 40 samples _40smpl, + /// 80 samples _80smpl, + /// 120 samples _120smpl, + /// 160 samples _160smpl, + /// 200 samples _200smpl, + /// 240 samples _240smpl, + /// 280 samples _280smpl, + /// 1 sample _1smpl, } +/// LED polarity #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum LedPolarity { + /// Active high (a high output turns on the LED). ActiveHigh, + /// Active low (a low output turns on the LED). ActiveLow, } diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index ea0a1703..07a97001 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,3 +1,5 @@ +//! Quad Serial Peripheral Interface (QSPI) flash driver. + #![macro_use] use core::future::poll_fn; @@ -15,6 +17,7 @@ pub use crate::pac::qspi::ifconfig0::{ pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; use crate::{pac, Peripheral}; +/// Deep power-down config. pub struct DeepPowerDownConfig { /// Time required for entering DPM, in units of 16us pub enter_time: u16, @@ -22,37 +25,62 @@ pub struct DeepPowerDownConfig { pub exit_time: u16, } +/// QSPI bus frequency. pub enum Frequency { + /// 32 Mhz M32 = 0, + /// 16 Mhz M16 = 1, + /// 10.7 Mhz M10_7 = 2, + /// 8 Mhz M8 = 3, + /// 6.4 Mhz M6_4 = 4, + /// 5.3 Mhz M5_3 = 5, + /// 4.6 Mhz M4_6 = 6, + /// 4 Mhz M4 = 7, + /// 3.6 Mhz M3_6 = 8, + /// 3.2 Mhz M3_2 = 9, + /// 2.9 Mhz M2_9 = 10, + /// 2.7 Mhz M2_7 = 11, + /// 2.5 Mhz M2_5 = 12, + /// 2.3 Mhz M2_3 = 13, + /// 2.1 Mhz M2_1 = 14, + /// 2 Mhz M2 = 15, } +/// QSPI config. #[non_exhaustive] pub struct Config { + /// XIP offset. pub xip_offset: u32, + /// Opcode used for read operations. pub read_opcode: ReadOpcode, + /// Opcode used for write operations. pub write_opcode: WriteOpcode, + /// Page size for write operations. pub write_page_size: WritePageSize, + /// Configuration for deep power down. If None, deep power down is disabled. pub deep_power_down: Option, + /// QSPI bus frequency. pub frequency: Frequency, /// Value is specified in number of 16 MHz periods (62.5 ns) pub sck_delay: u8, /// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3) pub spi_mode: SpiMode, + /// Addressing mode (24-bit or 32-bit) pub address_mode: AddressMode, } @@ -72,20 +100,24 @@ impl Default for Config { } } +/// Error #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// Operation address was out of bounds. OutOfBounds, // TODO add "not in data memory" error and check for it } +/// QSPI flash driver. pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, } impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { + /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -183,6 +215,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } } + /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -198,6 +231,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Do a custom QSPI instruction, blocking version. pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let len = core::cmp::max(req.len(), resp.len()) as u8; self.custom_instruction_start(opcode, req, len)?; @@ -346,6 +380,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Read data from the flash memory. pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -357,6 +392,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Write data to the flash memory. pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -368,6 +404,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Erase a sector on the flash memory. pub async fn erase(&mut self, address: usize) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -379,18 +416,21 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Read data from the flash memory, blocking version. pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { self.start_read(address, data)?; self.blocking_wait_ready(); Ok(()) } + /// Write data to the flash memory, blocking version. pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { self.start_write(address, data)?; self.blocking_wait_ready(); Ok(()) } + /// Erase a sector on the flash memory, blocking version. pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { self.start_erase(address)?; self.blocking_wait_ready(); @@ -547,7 +587,9 @@ pub(crate) mod sealed { } } +/// QSPI peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index e0caeaae..b0b3a8eb 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,3 +1,5 @@ +//! Random Number Generator (RNG) driver. + use core::future::poll_fn; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; @@ -128,10 +130,11 @@ impl<'d> Rng<'d> { /// However, this makes the generation of numbers slower. /// /// Defaults to disabled. - pub fn bias_correction(&self, enable: bool) { + pub fn set_bias_correction(&self, enable: bool) { RNG::regs().config.write(|w| w.dercen().bit(enable)) } + /// Fill the buffer with random bytes. pub async fn fill_bytes(&mut self, dest: &mut [u8]) { if dest.len() == 0 { return; // Nothing to fill @@ -175,6 +178,7 @@ impl<'d> Rng<'d> { drop(on_drop); } + /// Fill the buffer with random bytes, blocking version. pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) { self.start(); diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 4592d468..2d01a3dd 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -1,3 +1,5 @@ +//! Successive Approximation Analog-to-Digital Converter (SAADC) driver. + #![macro_use] use core::future::poll_fn; @@ -20,6 +22,7 @@ use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; +/// SAADC error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -102,17 +105,17 @@ impl<'d> ChannelConfig<'d> { } } -/// The state of a continuously running sampler. While it reflects -/// the progress of a sampler, it also signals what should be done -/// next. For example, if the sampler has stopped then the Saadc implementation -/// can then tear down its infrastructure. +/// Value returned by the SAADC callback, deciding what happens next. #[derive(PartialEq)] -pub enum SamplerState { - Sampled, - Stopped, +pub enum CallbackResult { + /// The SAADC should keep sampling and calling the callback. + Continue, + /// The SAADC should stop sampling, and return. + Stop, } impl<'d, const N: usize> Saadc<'d, N> { + /// Create a new SAADC driver. pub fn new( saadc: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -285,7 +288,7 @@ impl<'d, const N: usize> Saadc<'d, N> { /// free the buffers from being used by the peripheral. Cancellation will /// also cause the sampling to be stopped. - pub async fn run_task_sampler( + pub async fn run_task_sampler( &mut self, timer: &mut T, ppi_ch1: &mut impl ConfigurableChannel, @@ -293,9 +296,9 @@ impl<'d, const N: usize> Saadc<'d, N> { frequency: Frequency, sample_counter: u32, bufs: &mut [[[i16; N]; N0]; 2], - sampler: S, + callback: F, ) where - S: FnMut(&[[i16; N]]) -> SamplerState, + F: FnMut(&[[i16; N]]) -> CallbackResult, { let r = Self::regs(); @@ -321,20 +324,20 @@ impl<'d, const N: usize> Saadc<'d, N> { || { sample_ppi.enable(); }, - sampler, + callback, ) .await; } - async fn run_sampler( + async fn run_sampler( &mut self, bufs: &mut [[[i16; N]; N0]; 2], sample_rate_divisor: Option, mut init: I, - mut sampler: S, + mut callback: F, ) where I: FnMut(), - S: FnMut(&[[i16; N]]) -> SamplerState, + F: FnMut(&[[i16; N]]) -> CallbackResult, { // In case the future is dropped, stop the task and wait for it to end. let on_drop = OnDrop::new(Self::stop_sampling_immediately); @@ -395,12 +398,15 @@ impl<'d, const N: usize> Saadc<'d, N> { r.events_end.reset(); r.intenset.write(|w| w.end().set()); - if sampler(&bufs[current_buffer]) == SamplerState::Sampled { - let next_buffer = 1 - current_buffer; - current_buffer = next_buffer; - } else { - return Poll::Ready(()); - }; + match callback(&bufs[current_buffer]) { + CallbackResult::Continue => { + let next_buffer = 1 - current_buffer; + current_buffer = next_buffer; + } + CallbackResult::Stop => { + return Poll::Ready(()); + } + } } if r.events_started.read().bits() != 0 { @@ -458,7 +464,7 @@ impl<'d> Saadc<'d, 1> { sample_rate_divisor: u16, sampler: S, ) where - S: FnMut(&[[i16; 1]]) -> SamplerState, + S: FnMut(&[[i16; 1]]) -> CallbackResult, { self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await; } @@ -658,6 +664,10 @@ pub(crate) mod sealed { /// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal. pub trait Input: sealed::Input + Into + Peripheral

+ Sized + 'static { + /// Convert this SAADC input to a type-erased `AnyInput`. + /// + /// This allows using several inputs in situations that might require + /// them to be the same type, like putting them in an array. fn degrade_saadc(self) -> AnyInput { AnyInput { channel: self.channel(), @@ -665,6 +675,10 @@ pub trait Input: sealed::Input + Into + Peripheral

+ Sized + } } +/// A type-erased SAADC input. +/// +/// This allows using several inputs in situations that might require +/// them to be the same type, like putting them in an array. pub struct AnyInput { channel: InputChannel, } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 7bb4e39f..17e43578 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,3 +1,5 @@ +//! Serial Peripheral Instance in master mode (SPIM) driver. + #![macro_use] use core::future::poll_fn; @@ -16,27 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; +/// SPIM error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// RX buffer was too long. RxBufferTooLong, /// EasyDMA can only read from data memory, read only buffers in flash will fail. - DMABufferNotInDataMemory, + BufferNotInRAM, } -/// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// SPIM driver. pub struct Spim<'d, T: Instance> { _p: PeripheralRef<'d, T>, } +/// SPIM configuration. #[non_exhaustive] pub struct Config { + /// Frequency pub frequency: Frequency, + + /// SPI mode pub mode: Mode, + + /// Overread character. + /// + /// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer, + /// this byte will be transmitted in the MOSI line for the left-over bytes. pub orc: u8, } @@ -51,6 +63,7 @@ impl Default for Config { } impl<'d, T: Instance> Spim<'d, T> { + /// Create a new SPIM driver. pub fn new( spim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -70,6 +83,7 @@ impl<'d, T: Instance> Spim<'d, T> { ) } + /// Create a new SPIM driver, capable of TX only (MOSI only). pub fn new_txonly( spim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -81,6 +95,7 @@ impl<'d, T: Instance> Spim<'d, T> { Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) } + /// Create a new SPIM driver, capable of RX only (MISO only). pub fn new_rxonly( spim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -194,7 +209,7 @@ impl<'d, T: Instance> Spim<'d, T> { } fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable // slice can only be built from data located in RAM. @@ -236,7 +251,7 @@ impl<'d, T: Instance> Spim<'d, T> { fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { match self.blocking_inner_from_ram(rx, tx) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying SPIM tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; tx_ram_buf.copy_from_slice(tx); @@ -268,7 +283,7 @@ impl<'d, T: Instance> Spim<'d, T> { async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { match self.async_inner_from_ram(rx, tx).await { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying SPIM tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; tx_ram_buf.copy_from_slice(tx); @@ -385,7 +400,9 @@ pub(crate) mod sealed { } } +/// SPIM peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -437,7 +454,7 @@ mod eh1 { match *self { Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, - Self::DMABufferNotInDataMemory => embedded_hal_1::spi::ErrorKind::Other, + Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other, } } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 44af61a1..1b743647 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -1,3 +1,5 @@ +//! Serial Peripheral Instance in slave mode (SPIS) driver. + #![macro_use] use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; @@ -14,28 +16,43 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; +/// SPIS error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// RX buffer was too long. RxBufferTooLong, /// EasyDMA can only read from data memory, read only buffers in flash will fail. - DMABufferNotInDataMemory, + BufferNotInRAM, } -/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// SPIS driver. pub struct Spis<'d, T: Instance> { _p: PeripheralRef<'d, T>, } +/// SPIS configuration. #[non_exhaustive] pub struct Config { + /// SPI mode pub mode: Mode, + + /// Overread character. + /// + /// If the master keeps clocking the bus after all the bytes in the TX buffer have + /// already been transmitted, this byte will be constantly transmitted in the MISO line. pub orc: u8, + + /// Default byte. + /// + /// This is the byte clocked out in the MISO line for ignored transactions (if the master + /// sets CSN low while the semaphore is owned by the firmware) pub def: u8, + + /// Automatically make the firmware side acquire the semaphore on transfer end. pub auto_acquire: bool, } @@ -51,6 +68,7 @@ impl Default for Config { } impl<'d, T: Instance> Spis<'d, T> { + /// Create a new SPIS driver. pub fn new( spis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -72,6 +90,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) } + /// Create a new SPIS driver, capable of TX only (MISO only). pub fn new_txonly( spis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -92,6 +111,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) } + /// Create a new SPIS driver, capable of RX only (MOSI only). pub fn new_rxonly( spis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -212,7 +232,7 @@ impl<'d, T: Instance> Spis<'d, T> { } fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable // slice can only be built from data located in RAM. @@ -267,7 +287,7 @@ impl<'d, T: Instance> Spis<'d, T> { fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { match self.blocking_inner_from_ram(rx, tx) { Ok(n) => Ok(n), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying SPIS tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; tx_ram_buf.copy_from_slice(tx); @@ -330,7 +350,7 @@ impl<'d, T: Instance> Spis<'d, T> { async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { match self.async_inner_from_ram(rx, tx).await { Ok(n) => Ok(n), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying SPIS tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; tx_ram_buf.copy_from_slice(tx); @@ -468,7 +488,9 @@ pub(crate) mod sealed { } } +/// SPIS peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 7a7f61b5..5298faab 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -1,4 +1,4 @@ -//! Temperature sensor interface. +//! Builtin temperature sensor driver. use core::future::poll_fn; use core::task::Poll; @@ -12,7 +12,7 @@ use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; -/// Integrated temperature sensor. +/// Builtin temperature sensor driver. pub struct Temp<'d> { _irq: PeripheralRef<'d, interrupt::TEMP>, } @@ -20,6 +20,7 @@ pub struct Temp<'d> { static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Temp<'d> { + /// Create a new temperature sensor driver. pub fn new(_t: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { into_ref!(_t, irq); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index bc871064..d1ae5723 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -1,3 +1,9 @@ +//! Timer driver. +//! +//! Important note! This driver is very low level. For most time-related use cases, like +//! "sleep for X seconds", "do something every X seconds", or measuring time, you should +//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead! + #![macro_use] use core::future::poll_fn; @@ -28,9 +34,13 @@ pub(crate) mod sealed { pub trait TimerType {} } +/// Basic Timer instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } + +/// Extended timer instance. pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} macro_rules! impl_timer { @@ -61,18 +71,28 @@ macro_rules! impl_timer { }; } +/// Timer frequency #[repr(u8)] pub enum Frequency { - // I'd prefer not to prefix these with `F`, but Rust identifiers can't start with digits. + /// 16MHz F16MHz = 0, + /// 8MHz F8MHz = 1, + /// 4MHz F4MHz = 2, + /// 2MHz F2MHz = 3, + /// 1MHz F1MHz = 4, + /// 500kHz F500kHz = 5, + /// 250kHz F250kHz = 6, + /// 125kHz F125kHz = 7, + /// 62500Hz F62500Hz = 8, + /// 31250Hz F31250Hz = 9, } @@ -86,7 +106,10 @@ pub enum Frequency { pub trait TimerType: sealed::TimerType {} +/// Marker type indicating the timer driver can await expiration (it owns the timer interrupt). pub enum Awaitable {} + +/// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt). pub enum NotAwaitable {} impl sealed::TimerType for Awaitable {} @@ -94,12 +117,14 @@ impl sealed::TimerType for NotAwaitable {} impl TimerType for Awaitable {} impl TimerType for NotAwaitable {} +/// Timer driver. pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { _p: PeripheralRef<'d, T>, _i: PhantomData, } impl<'d, T: Instance> Timer<'d, T, Awaitable> { + /// Create a new async-capable timer driver. pub fn new_awaitable(timer: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { into_ref!(irq); @@ -107,16 +132,17 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> { irq.unpend(); irq.enable(); - Self::new_irqless(timer) + Self::new_inner(timer) } } + impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { - /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. + /// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new(timer: impl Peripheral

+ 'd) -> Self { - Self::new_irqless(timer) + Self::new_inner(timer) } } @@ -124,7 +150,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. /// /// This is used by the public constructors. - fn new_irqless(timer: impl Peripheral

+ 'd) -> Self { + fn new_inner(timer: impl Peripheral

+ 'd) -> Self { into_ref!(timer); let regs = T::regs(); diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 4eafd18c..0dcb2b0d 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -1,11 +1,7 @@ +//! I2C-compatible Two Wire Interface in master mode (TWIM) driver. + #![macro_use] -//! HAL interface to the TWIM peripheral. -//! -//! See product specification: -//! -//! - nRF52832: Section 33 -//! - nRF52840: Section 6.31 use core::future::{poll_fn, Future}; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; @@ -23,22 +19,39 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Peripheral}; +/// TWI frequency #[derive(Clone, Copy)] pub enum Frequency { - #[doc = "26738688: 100 kbps"] + /// 100 kbps K100 = 26738688, - #[doc = "67108864: 250 kbps"] + /// 250 kbps K250 = 67108864, - #[doc = "104857600: 400 kbps"] + /// 400 kbps K400 = 104857600, } +/// TWIM config. #[non_exhaustive] pub struct Config { + /// Frequency pub frequency: Frequency, + + /// Enable high drive for the SDA line. pub sda_high_drive: bool, + + /// Enable internal pullup for the SDA line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub sda_pullup: bool, + + /// Enable high drive for the SCL line. pub scl_high_drive: bool, + + /// Enable internal pullup for the SCL line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub scl_pullup: bool, } @@ -54,29 +67,38 @@ impl Default for Config { } } +/// TWI error. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// RX buffer was too long. RxBufferTooLong, + /// Data transmit failed. Transmit, + /// Data reception failed. Receive, - DMABufferNotInDataMemory, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly. AddressNack, + /// Didn't receive an ACK bit after a data byte. DataNack, + /// Overrun error. Overrun, + /// Timeout error. Timeout, } -/// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// TWI driver. pub struct Twim<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> Twim<'d, T> { + /// Create a new TWI driver. pub fn new( twim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -153,7 +175,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); @@ -233,7 +255,7 @@ impl<'d, T: Instance> Twim<'d, T> { return Err(Error::DataNack); } if err.overrun().is_received() { - return Err(Error::DataNack); + return Err(Error::Overrun); } Ok(()) } @@ -435,7 +457,7 @@ impl<'d, T: Instance> Twim<'d, T> { ) -> Result<(), Error> { match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying TWIM tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; tx_ram_buf.copy_from_slice(wr_buffer); @@ -448,7 +470,7 @@ impl<'d, T: Instance> Twim<'d, T> { fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { match self.setup_write_from_ram(address, wr_buffer, inten) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying TWIM tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; tx_ram_buf.copy_from_slice(wr_buffer); @@ -612,6 +634,10 @@ impl<'d, T: Instance> Twim<'d, T> { // =========================================== + /// Read from an I2C slave. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { self.setup_read(address, buffer, true)?; self.async_wait().await; @@ -621,6 +647,10 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Write to an I2C slave. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { self.setup_write(address, buffer, true)?; self.async_wait().await; @@ -640,6 +670,11 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Write data to an I2C slave, then read data from the slave without + /// triggering a stop condition between the two. + /// + /// The buffers must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { self.setup_write_read(address, wr_buffer, rd_buffer, true)?; self.async_wait().await; @@ -705,7 +740,9 @@ pub(crate) mod sealed { } } +/// TWIM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -776,7 +813,7 @@ mod eh1 { Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, - Self::DMABufferNotInDataMemory => embedded_hal_1::i2c::ErrorKind::Other, + Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other, Self::AddressNack => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 4091b017..51a70c30 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -1,11 +1,7 @@ +//! I2C-compatible Two Wire Interface in slave mode (TWIM) driver. + #![macro_use] -//! HAL interface to the TWIS peripheral. -//! -//! See product specification: -//! -//! - nRF52832: Section 33 -//! - nRF52840: Section 6.31 use core::future::{poll_fn, Future}; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; @@ -22,14 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::slice_in_ram_or; use crate::{gpio, pac, Peripheral}; +/// TWIS config. #[non_exhaustive] pub struct Config { + /// First address pub address0: u8, + + /// Second address, optional. pub address1: Option, + + /// Overread character. + /// + /// If the master keeps clocking the bus after all the bytes in the TX buffer have + /// already been transmitted, this byte will be constantly transmitted. pub orc: u8, + + /// Enable high drive for the SDA line. pub sda_high_drive: bool, + + /// Enable internal pullup for the SDA line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub sda_pullup: bool, + + /// Enable high drive for the SCL line. pub scl_high_drive: bool, + + /// Enable internal pullup for the SCL line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub scl_pullup: bool, } @@ -54,36 +73,48 @@ enum Status { Write, } +/// TWIS error. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// TX buffer was too long. RxBufferTooLong, + /// Didn't receive an ACK bit after a data byte. DataNack, + /// Bus error. Bus, - DMABufferNotInDataMemory, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// Overflow Overflow, + /// Overread OverRead, + /// Timeout Timeout, } +/// Received command #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Command { + /// Read Read, + /// Write+read WriteRead(usize), + /// Write Write(usize), } -/// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// TWIS driver. pub struct Twis<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> Twis<'d, T> { + /// Create a new TWIS driver. pub fn new( twis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -174,7 +205,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); @@ -535,7 +566,7 @@ impl<'d, T: Instance> Twis<'d, T> { fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { match self.setup_respond_from_ram(wr_buffer, inten) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying TWIS tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; tx_ram_buf.copy_from_slice(wr_buffer); @@ -737,7 +768,9 @@ pub(crate) mod sealed { } } +/// TWIS peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 031cf82f..48457744 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1,8 +1,6 @@ -#![macro_use] - -//! Async UART +//! Universal Asynchronous Receiver Transmitter (UART) driver. //! -//! Async UART is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte]. +//! The UART driver is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte]. //! The [Uarte] here is useful for those use-cases where reading the UARTE peripheral is //! exclusively awaited on. If the [Uarte] is required to be awaited on with some other future, //! for example when using `futures_util::future::select`, then you should consider @@ -13,6 +11,8 @@ //! memory may be used given that buffers are passed in directly to its read and write //! methods. +#![macro_use] + use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -32,10 +32,13 @@ use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; use crate::{pac, Peripheral}; +/// UARTE config. #[derive(Clone)] #[non_exhaustive] pub struct Config { + /// Parity bit. pub parity: Parity, + /// Baud rate. pub baudrate: Baudrate, } @@ -48,31 +51,33 @@ impl Default for Config { } } +/// UART error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// Buffer was too long. BufferTooLong, - DMABufferNotInDataMemory, - // TODO: add other error variants. + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, } -/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// UARTE driver. pub struct Uarte<'d, T: Instance> { tx: UarteTx<'d, T>, rx: UarteRx<'d, T>, } -/// Transmitter interface to the UARTE peripheral obtained -/// via [Uarte]::split. +/// Transmitter part of the UARTE driver. +/// +/// This can be obtained via [`Uarte::split`], or created directly. pub struct UarteTx<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -/// Receiver interface to the UARTE peripheral obtained -/// via [Uarte]::split. +/// Receiver part of the UARTE driver. +/// +/// This can be obtained via [`Uarte::split`], or created directly. pub struct UarteRx<'d, T: Instance> { _p: PeripheralRef<'d, T>, } @@ -165,16 +170,16 @@ impl<'d, T: Instance> Uarte<'d, T> { } } - /// Split the Uarte into a transmitter and receiver, which is - /// particularly useful when having two tasks correlating to - /// transmitting and receiving. + /// Split the Uarte into the transmitter and receiver parts. + /// + /// This is useful to concurrently transmit and receive from independent tasks. pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { (self.tx, self.rx) } - /// Split the Uarte into a transmitter and receiver that will - /// return on idle, which is determined as the time it takes - /// for two bytes to be received. + /// Split the Uarte into the transmitter and receiver with idle support parts. + /// + /// This is useful to concurrently transmit and receive from independent tasks. pub fn split_with_idle( self, timer: impl Peripheral

+ 'd, @@ -247,10 +252,12 @@ impl<'d, T: Instance> Uarte<'d, T> { } } + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.read(buffer).await } + /// Write all bytes in the buffer. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.write(buffer).await } @@ -260,10 +267,12 @@ impl<'d, T: Instance> Uarte<'d, T> { self.tx.write_from_ram(buffer).await } + /// Read bytes until the buffer is filled. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.blocking_read(buffer) } + /// Write all bytes in the buffer. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) } @@ -355,10 +364,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { Self { _p: uarte } } + /// Write all bytes in the buffer. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { match self.write_from_ram(buffer).await { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying UARTE tx buffer into RAM for DMA"); let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; ram_buf.copy_from_slice(buffer); @@ -368,12 +378,13 @@ impl<'d, T: Instance> UarteTx<'d, T> { } } + /// Same as [`write`](Self::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); } - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -423,10 +434,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { Ok(()) } + /// Write all bytes in the buffer. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { match self.blocking_write_from_ram(buffer) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying UARTE tx buffer into RAM for DMA"); let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; ram_buf.copy_from_slice(buffer); @@ -436,12 +448,13 @@ impl<'d, T: Instance> UarteTx<'d, T> { } } + /// Same as [`write_from_ram`](Self::write_from_ram) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); } - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -549,6 +562,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { Self { _p: uarte } } + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); @@ -602,6 +616,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { Ok(()) } + /// Read bytes until the buffer is filled. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); @@ -653,6 +668,9 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { } } +/// Receiver part of the UARTE driver, with `read_until_idle` support. +/// +/// This can be obtained via [`Uarte::split_with_idle`]. pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { rx: UarteRx<'d, T>, timer: Timer<'d, U>, @@ -661,16 +679,21 @@ pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { } impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.ppi_ch1.disable(); self.rx.read(buffer).await } + /// Read bytes until the buffer is filled. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.ppi_ch1.disable(); self.rx.blocking_read(buffer) } + /// Read bytes until the buffer is filled, or the line becomes idle. + /// + /// Returns the amount of bytes read. pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { return Ok(0); @@ -727,6 +750,9 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { Ok(n) } + /// Read bytes until the buffer is filled, or the line becomes idle. + /// + /// Returns the amount of bytes read. pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { return Ok(0); @@ -860,7 +886,9 @@ pub(crate) mod sealed { } } +/// UARTE peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -919,7 +947,7 @@ mod eh1 { fn kind(&self) -> embedded_hal_1::serial::ErrorKind { match *self { Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::DMABufferNotInDataMemory => embedded_hal_1::serial::ErrorKind::Other, + Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other, } } } diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index f030b909..cd142f00 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,3 +1,5 @@ +//! Universal Serial Bus (USB) driver. + #![macro_use] use core::future::poll_fn; @@ -24,38 +26,38 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); +/// Trait for detecting USB VBUS power. +/// /// There are multiple ways to detect USB power. The behavior /// here provides a hook into determining whether it is. -pub trait UsbSupply { +pub trait VbusDetect { + /// Report whether power is detected. + /// + /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the + /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. fn is_usb_detected(&self) -> bool; + + /// Wait until USB power is ready. + /// + /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the + /// `USBPWRRDY` event from the `POWER` peripheral. async fn wait_power_ready(&mut self) -> Result<(), ()>; } -pub struct Driver<'d, T: Instance, P: UsbSupply> { - _p: PeripheralRef<'d, T>, - alloc_in: Allocator, - alloc_out: Allocator, - usb_supply: P, -} - -/// Uses the POWER peripheral to detect when power is available -/// for USB. Unsuitable for usage with the nRF softdevice. +/// [`VbusDetect`] implementation using the native hardware POWER peripheral. +/// +/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces +/// to POWER. In that case, use [`VbusDetectSignal`]. #[cfg(not(feature = "_nrf5340-app"))] -pub struct PowerUsb { +pub struct HardwareVbusDetect { _private: (), } -/// Can be used to signal that power is available. Particularly suited for -/// use with the nRF softdevice. -pub struct SignalledSupply { - usb_detected: AtomicBool, - power_ready: AtomicBool, -} - static POWER_WAKER: AtomicWaker = NEW_AW; #[cfg(not(feature = "_nrf5340-app"))] -impl PowerUsb { +impl HardwareVbusDetect { + /// Create a new `VbusDetectNative`. pub fn new(power_irq: impl Interrupt) -> Self { let regs = unsafe { &*pac::POWER::ptr() }; @@ -92,7 +94,7 @@ impl PowerUsb { } #[cfg(not(feature = "_nrf5340-app"))] -impl UsbSupply for PowerUsb { +impl VbusDetect for HardwareVbusDetect { fn is_usb_detected(&self) -> bool { let regs = unsafe { &*pac::POWER::ptr() }; regs.usbregstatus.read().vbusdetect().is_vbus_present() @@ -115,7 +117,20 @@ impl UsbSupply for PowerUsb { } } -impl SignalledSupply { +/// Software-backed [`VbusDetect`] implementation. +/// +/// This implementation does not interact with the hardware, it allows user code +/// to notify the power events by calling functions instead. +/// +/// This is suitable for use with the nRF softdevice, by calling the functions +/// when the softdevice reports power-related events. +pub struct SoftwareVbusDetect { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +impl SoftwareVbusDetect { + /// Create a new `SoftwareVbusDetect`. pub fn new(usb_detected: bool, power_ready: bool) -> Self { BUS_WAKER.wake(); @@ -125,6 +140,9 @@ impl SignalledSupply { } } + /// Report whether power was detected. + /// + /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. pub fn detected(&self, detected: bool) { self.usb_detected.store(detected, Ordering::Relaxed); self.power_ready.store(false, Ordering::Relaxed); @@ -132,13 +150,16 @@ impl SignalledSupply { POWER_WAKER.wake(); } + /// Report when USB power is ready. + /// + /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. pub fn ready(&self) { self.power_ready.store(true, Ordering::Relaxed); POWER_WAKER.wake(); } } -impl UsbSupply for &SignalledSupply { +impl VbusDetect for &SoftwareVbusDetect { fn is_usb_detected(&self) -> bool { self.usb_detected.load(Ordering::Relaxed) } @@ -159,7 +180,16 @@ impl UsbSupply for &SignalledSupply { } } -impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { +/// USB driver. +pub struct Driver<'d, T: Instance, P: VbusDetect> { + _p: PeripheralRef<'d, T>, + alloc_in: Allocator, + alloc_out: Allocator, + usb_supply: P, +} + +impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { + /// Create a new USB driver. pub fn new(usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, usb_supply: P) -> Self { into_ref!(usb, irq); irq.set_handler(Self::on_interrupt); @@ -225,7 +255,7 @@ impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { } } -impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> { +impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; type ControlPipe = ControlPipe<'d, T>; @@ -278,13 +308,14 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> } } -pub struct Bus<'d, T: Instance, P: UsbSupply> { +/// USB bus. +pub struct Bus<'d, T: Instance, P: VbusDetect> { _p: PeripheralRef<'d, T>, power_available: bool, usb_supply: P, } -impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { +impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { async fn enable(&mut self) { let regs = T::regs(); @@ -513,7 +544,10 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } } +/// Type-level marker for OUT endpoints. pub enum Out {} + +/// Type-level marker for IN endpoints. pub enum In {} trait EndpointDir { @@ -556,6 +590,7 @@ impl EndpointDir for Out { } } +/// USB endpoint. pub struct Endpoint<'d, T: Instance, Dir> { _phantom: PhantomData<(&'d mut T, Dir)>, info: EndpointInfo, @@ -715,6 +750,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { } } +/// USB control pipe. pub struct ControlPipe<'d, T: Instance> { _p: PeripheralRef<'d, T>, max_packet_size: u16, @@ -905,7 +941,9 @@ pub(crate) mod sealed { } } +/// USB peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index 330ca98b..40a67442 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -1,4 +1,4 @@ -//! HAL interface to the WDT peripheral. +//! Watchdog Timer (WDT) driver. //! //! This HAL implements a basic watchdog timer with 1..=8 handles. //! Once the watchdog has been started, it cannot be stopped. @@ -8,6 +8,7 @@ use crate::peripherals; const MIN_TICKS: u32 = 15; +/// WDT configuration. #[non_exhaustive] pub struct Config { /// Number of 32768 Hz ticks in each watchdog period. @@ -57,13 +58,13 @@ impl Default for Config { } } -/// An interface to the Watchdog. +/// Watchdog driver. pub struct Watchdog { _private: (), } impl Watchdog { - /// Try to create a new watchdog instance from the peripheral. + /// Try to create a new watchdog driver. /// /// This function will return an error if the watchdog is already active /// with a `config` different to the requested one, or a different number of @@ -155,6 +156,7 @@ impl Watchdog { } } +/// Watchdog handle. pub struct WatchdogHandle { index: u8, } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index bbd8a5d2..95d93987 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.4.0" diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs index 3cca005b..52d46e4f 100644 --- a/examples/nrf52840/src/bin/i2s_effect.rs +++ b/examples/nrf52840/src/bin/i2s_effect.rs @@ -24,9 +24,9 @@ async fn main(_spawner: Spawner) { let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - let config = Config::default() - .sample_width(SampleWidth::_16bit) - .channels(Channels::MonoLeft); + let mut config = Config::default(); + config.sample_width = SampleWidth::_16bit; + config.channels = Channels::MonoLeft; let irq = interrupt::take!(I2S); let buffers_out = MultiBuffering::::new(); diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs index 48eb7d58..5ebfd954 100644 --- a/examples/nrf52840/src/bin/i2s_monitor.rs +++ b/examples/nrf52840/src/bin/i2s_monitor.rs @@ -22,9 +22,9 @@ async fn main(_spawner: Spawner) { let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - let config = Config::default() - .sample_width(SampleWidth::_16bit) - .channels(Channels::MonoLeft); + let mut config = Config::default(); + config.sample_width = SampleWidth::_16bit; + config.channels = Channels::MonoLeft; let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs index 1b0e8ebc..eda93067 100644 --- a/examples/nrf52840/src/bin/i2s_waveform.rs +++ b/examples/nrf52840/src/bin/i2s_waveform.rs @@ -23,9 +23,9 @@ async fn main(_spawner: Spawner) { let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - let config = Config::default() - .sample_width(SampleWidth::_16bit) - .channels(Channels::MonoLeft); + let mut config = Config::default(); + config.sample_width = SampleWidth::_16bit; + config.channels = Channels::MonoLeft; let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index bb50ac65..2551d15f 100644 --- a/examples/nrf52840/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; +use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; use embassy_nrf::timer::Frequency; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; @@ -61,7 +61,7 @@ async fn main(_p: Spawner) { c = 0; a = 0; } - SamplerState::Sampled + CallbackResult::Continue }, ) .await; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index a8d53e46..699666ce 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -9,7 +9,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; @@ -18,7 +18,7 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; +type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; macro_rules! singleton { ($val:expr) => {{ @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 76e19871..017cac19 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -10,7 +10,7 @@ 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}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; @@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 4916a38d..a5849129 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 7c9c4184..18b6f25b 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -7,7 +7,7 @@ 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::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -97,7 +97,7 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( +async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 93efc2fe..3532d3f8 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -6,7 +6,7 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -14,7 +14,7 @@ use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; +type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { @@ -42,7 +42,7 @@ async fn main(spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); From a697f1517a9c54ba042bbf70e0b2ed762d300471 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 17:29:34 -0600 Subject: [PATCH 316/374] Set `poll_fn` in `TaskStorage::new` --- embassy-executor/src/raw/mod.rs | 35 +++++++++++++------------------- embassy-executor/src/raw/util.rs | 6 ------ 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 183c5e6a..8cdce92e 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -46,8 +46,8 @@ pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 - pub(crate) poll_fn: UninitCell, // Valid if STATE_SPAWNED + pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + poll_fn: unsafe fn(TaskRef), #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Cell, @@ -55,22 +55,6 @@ pub(crate) struct TaskHeader { pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } -impl TaskHeader { - const fn new() -> Self { - Self { - state: AtomicU32::new(0), - run_queue_item: RunQueueItem::new(), - executor: Cell::new(ptr::null()), - poll_fn: UninitCell::uninit(), - - #[cfg(feature = "integrated-timers")] - expires_at: Cell::new(Instant::from_ticks(0)), - #[cfg(feature = "integrated-timers")] - timer_queue_item: timer_queue::TimerQueueItem::new(), - } - } -} - /// This is essentially a `&'static TaskStorage` where the type of the future has been erased. #[derive(Clone, Copy)] pub struct TaskRef { @@ -128,7 +112,17 @@ impl TaskStorage { /// Create a new TaskStorage, in not-spawned state. pub const fn new() -> Self { Self { - raw: TaskHeader::new(), + raw: TaskHeader { + state: AtomicU32::new(0), + run_queue_item: RunQueueItem::new(), + executor: Cell::new(ptr::null()), + poll_fn: Self::poll, + + #[cfg(feature = "integrated-timers")] + expires_at: Cell::new(Instant::from_ticks(0)), + #[cfg(feature = "integrated-timers")] + timer_queue_item: timer_queue::TimerQueueItem::new(), + }, future: UninitCell::uninit(), } } @@ -164,7 +158,6 @@ impl TaskStorage { unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> TaskRef { // Initialize the task - self.raw.poll_fn.write(Self::poll); self.future.write(future()); TaskRef::new(self) } @@ -405,7 +398,7 @@ impl Executor { trace::task_exec_begin(p.as_ptr() as u32); // Run the task - task.poll_fn.read()(p); + (task.poll_fn)(p); #[cfg(feature = "rtos-trace")] trace::task_exec_end(); diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index ed582218..2b1f6b6f 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -25,9 +25,3 @@ impl UninitCell { ptr::drop_in_place(self.as_mut_ptr()) } } - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} From fb1946be7fa38eecb36711a1257f89dae3714b61 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 17:49:18 -0600 Subject: [PATCH 317/374] Replace the pointer in `TaskHeader` with an `Option<&Executor>` --- embassy-executor/src/raw/mod.rs | 10 +++++----- embassy-executor/src/spawner.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 8cdce92e..6783c485 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -15,10 +15,10 @@ mod waker; use core::cell::Cell; use core::future::Future; +use core::mem; use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; -use core::{mem, ptr}; use atomic_polyfill::{AtomicU32, Ordering}; use critical_section::CriticalSection; @@ -46,7 +46,7 @@ pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + pub(crate) executor: Cell>, poll_fn: unsafe fn(TaskRef), #[cfg(feature = "integrated-timers")] @@ -115,7 +115,7 @@ impl TaskStorage { raw: TaskHeader { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), - executor: Cell::new(ptr::null()), + executor: Cell::new(None), poll_fn: Self::poll, #[cfg(feature = "integrated-timers")] @@ -346,7 +346,7 @@ impl Executor { /// In this case, the task's Future must be Send. This is because this is effectively /// sending the task to the executor thread. pub(super) unsafe fn spawn(&'static self, task: TaskRef) { - task.header().executor.set(self); + task.header().executor.set(Some(self)); #[cfg(feature = "rtos-trace")] trace::task_new(task.as_ptr() as u32); @@ -455,7 +455,7 @@ pub fn wake_task(task: TaskRef) { // We have just marked the task as scheduled, so enqueue it. unsafe { - let executor = &*header.executor.get(); + let executor = header.executor.get().unwrap_unchecked(); executor.enqueue(cs, task); } }) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 650ea06c..7c0a0183 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -89,10 +89,10 @@ impl Spawner { /// /// Panics if the current executor is not an Embassy executor. pub async fn for_current_executor() -> Self { - poll_fn(|cx| unsafe { + poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = task.header().executor.get(); - Poll::Ready(Self::new(&*executor)) + let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + Poll::Ready(Self::new(executor)) }) .await } @@ -165,10 +165,10 @@ impl SendSpawner { /// /// Panics if the current executor is not an Embassy executor. pub async fn for_current_executor() -> Self { - poll_fn(|cx| unsafe { + poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = task.header().executor.get(); - Poll::Ready(Self::new(&*executor)) + let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + Poll::Ready(Self::new(executor)) }) .await } From 4a8e9cf4d9f682bfe4942559da7e76315216c377 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 18:49:32 -0600 Subject: [PATCH 318/374] Add internal `AvailableTask` type --- embassy-executor/src/raw/mod.rs | 68 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 6783c485..e93e6036 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -141,25 +141,14 @@ impl TaskStorage { /// Once the task has finished running, you may spawn it again. It is allowed to spawn it /// on a different executor. pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - if self.spawn_mark_used() { - return unsafe { SpawnToken::::new(self.spawn_initialize(future)) }; + let task = AvailableTask::claim(self); + match task { + Some(task) => { + let task = task.initialize(future); + unsafe { SpawnToken::::new(task) } + } + None => SpawnToken::new_failed(), } - - SpawnToken::::new_failed() - } - - fn spawn_mark_used(&'static self) -> bool { - let state = STATE_SPAWNED | STATE_RUN_QUEUED; - self.raw - .state - .compare_exchange(0, state, Ordering::AcqRel, Ordering::Acquire) - .is_ok() - } - - unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> TaskRef { - // Initialize the task - self.future.write(future()); - TaskRef::new(self) } unsafe fn poll(p: TaskRef) { @@ -184,6 +173,27 @@ impl TaskStorage { unsafe impl Sync for TaskStorage {} +struct AvailableTask { + task: &'static TaskStorage, +} + +impl AvailableTask { + fn claim(task: &'static TaskStorage) -> Option { + task.raw + .state + .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire) + .ok() + .map(|_| Self { task }) + } + + fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { + unsafe { + self.task.future.write(future()); + } + TaskRef::new(self.task) + } +} + /// Raw storage that can hold up to N tasks of the same type. /// /// This is essentially a `[TaskStorage; N]`. @@ -207,13 +217,14 @@ impl TaskPool { /// is currently free. If none is free, a "poisoned" SpawnToken is returned, /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - for task in &self.pool { - if task.spawn_mark_used() { - return unsafe { SpawnToken::::new(task.spawn_initialize(future)) }; + let task = self.pool.iter().find_map(AvailableTask::claim); + match task { + Some(task) => { + let task = task.initialize(future); + unsafe { SpawnToken::::new(task) } } + None => SpawnToken::new_failed(), } - - SpawnToken::::new_failed() } /// Like spawn(), but allows the task to be send-spawned if the args are Send even if @@ -255,13 +266,14 @@ impl TaskPool { // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. - for task in &self.pool { - if task.spawn_mark_used() { - return SpawnToken::::new(task.spawn_initialize(future)); + let task = self.pool.iter().find_map(AvailableTask::claim); + match task { + Some(task) => { + let task = task.initialize(future); + unsafe { SpawnToken::::new(task) } } + None => SpawnToken::new_failed(), } - - SpawnToken::::new_failed() } } From 791fbb3ca0caf81882f67caea9e71adf43496261 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 21:42:45 -0600 Subject: [PATCH 319/374] Make `poll_fn` lazily initialized again --- embassy-executor/src/raw/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e93e6036..42bd8226 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -47,7 +47,7 @@ pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, pub(crate) executor: Cell>, - poll_fn: unsafe fn(TaskRef), + poll_fn: Cell>, #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Cell, @@ -116,7 +116,8 @@ impl TaskStorage { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), executor: Cell::new(None), - poll_fn: Self::poll, + // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` + poll_fn: Cell::new(None), #[cfg(feature = "integrated-timers")] expires_at: Cell::new(Instant::from_ticks(0)), @@ -188,6 +189,7 @@ impl AvailableTask { fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { unsafe { + self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write(future()); } TaskRef::new(self.task) @@ -410,7 +412,7 @@ impl Executor { trace::task_exec_begin(p.as_ptr() as u32); // Run the task - (task.poll_fn)(p); + task.poll_fn.get().unwrap_unchecked()(p); #[cfg(feature = "rtos-trace")] trace::task_exec_end(); From cb88dd285dd53a12545a0f01a6d43aaf1361b82a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Feb 2023 20:54:32 +0100 Subject: [PATCH 320/374] nrf/twis: FIx doc typo --- embassy-nrf/src/twis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 51a70c30..c514d9f2 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -80,7 +80,7 @@ enum Status { pub enum Error { /// TX buffer was too long. TxBufferTooLong, - /// TX buffer was too long. + /// RX buffer was too long. RxBufferTooLong, /// Didn't receive an ACK bit after a data byte. DataNack, From c4cbb89fcd60375c4982a61fc3d9e5183f97d748 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 2 Feb 2023 12:51:47 +0100 Subject: [PATCH 321/374] LoRa/SX126x: adjust Rx window offset and duration Those timings open Rx time windows covering 99.7% of the one expected by the antenna while allowing 3ms for the Rx subsystem to start listening. --- embassy-lora/src/sx126x/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index b14d422a..a9aeadfc 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -55,10 +55,10 @@ where BUS: Error + Format + 'static, { fn get_rx_window_offset_ms(&self) -> i32 { - -500 + -3 } fn get_rx_window_duration_ms(&self) -> u32 { - 800 + 1003 } } From ef4a20f67b925ffcbf83642e33b13d3605b78d46 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 2 Feb 2023 12:52:00 +0100 Subject: [PATCH 322/374] LoRa/STM32WL: adjust Rx window offset and duration Those timings open Rx time windows covering 99.7% of the one expected by the antenna while allowing 3ms for the Rx subsystem to start listening. --- embassy-lora/src/stm32wl/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 6ed63bf7..3d52c1cc 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -260,10 +260,10 @@ impl From for RadioError { impl<'d, RS> Timings for SubGhzRadio<'d, RS> { fn get_rx_window_offset_ms(&self) -> i32 { - -500 + -3 } fn get_rx_window_duration_ms(&self) -> u32 { - 3000 + 1003 } } From a432d91d82438f4ce0f17c85b9453ae79db5390d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 3 Feb 2023 06:36:10 +0100 Subject: [PATCH 323/374] PeripheralRef docs improvements. --- embassy-hal-common/src/peripheral.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs index f507468f..4a6b6a60 100644 --- a/embassy-hal-common/src/peripheral.rs +++ b/embassy-hal-common/src/peripheral.rs @@ -3,16 +3,17 @@ use core::ops::{Deref, DerefMut}; /// An exclusive reference to a peripheral. /// -/// This is functionally the same as a `&'a mut T`. The reason for having a -/// dedicated struct is memory efficiency: +/// This is functionally the same as a `&'a mut T`. There's a few advantages in having +/// a dedicated struct instead: /// -/// Peripheral singletons are typically either zero-sized (for concrete peripherals -/// like `PA9` or `Spi4`) or very small (for example `AnyPin` which is 1 byte). -/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized. -/// PeripheralRef stores a copy of `T` instead, so it's the same size. -/// -/// but it is the size of `T` not the size -/// of a pointer. This is useful if T is a zero sized type. +/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete +/// peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte). +/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized. +/// PeripheralRef stores a copy of `T` instead, so it's the same size. +/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`, +/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic +/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes +/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization. pub struct PeripheralRef<'a, T> { inner: T, _lifetime: PhantomData<&'a mut T>, From 0bb6000e5cab0a2f2dbbb95a4a2d69a2536de88b Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Thu, 2 Feb 2023 21:42:42 -0800 Subject: [PATCH 324/374] stm32 gpio implement degrade to AnyPin --- embassy-stm32/src/gpio.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 5e334675..3024f1ff 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -28,6 +28,21 @@ impl<'d, T: Pin> Flex<'d, T> { Self { pin } } + #[inline] + pub fn degrade(mut self) -> Flex<'d, AnyPin> { + // Safety: We are about to drop the other copy of this pin, so + // this clone is safe. + let pin = unsafe { self.pin.clone_unchecked() }; + + // We don't want to run the destructor here, because that would + // deconfigure the pin. + core::mem::forget(self); + + Flex { + pin: pin.map_into::(), + } + } + /// Put the pin into input mode. #[inline] pub fn set_as_input(&mut self, pull: Pull) { @@ -286,6 +301,13 @@ impl<'d, T: Pin> Input<'d, T> { Self { pin } } + #[inline] + pub fn degrade(self) -> Input<'d, AnyPin> { + Input { + pin: self.pin.degrade(), + } + } + #[inline] pub fn is_high(&self) -> bool { self.pin.is_high() @@ -345,6 +367,13 @@ impl<'d, T: Pin> Output<'d, T> { Self { pin } } + #[inline] + pub fn degrade(self) -> Output<'d, AnyPin> { + Output { + pin: self.pin.degrade(), + } + } + /// Set the output as high. #[inline] pub fn set_high(&mut self) { @@ -407,6 +436,13 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { Self { pin } } + #[inline] + pub fn degrade(self) -> Output<'d, AnyPin> { + Output { + pin: self.pin.degrade(), + } + } + #[inline] pub fn is_high(&self) -> bool { !self.pin.is_low() From 64ebb9b7fee1fec62eacc1064a7458a6e6b07048 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Mon, 6 Feb 2023 09:44:13 +1100 Subject: [PATCH 325/374] stm32/usart: implement stop_bits configuration --- embassy-stm32/src/usart/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 20f4eede..b18b646b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -770,7 +770,14 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable unsafe { r.brr().write_value(regs::Brr(div)); - r.cr2().write(|_w| {}); + r.cr2().write(|w| { + w.set_stop(match config.stop_bits { + StopBits::STOP0P5 => vals::Stop::STOP0P5, + StopBits::STOP1 => vals::Stop::STOP1, + StopBits::STOP1P5 => vals::Stop::STOP1P5, + StopBits::STOP2 => vals::Stop::STOP2, + }); + }); r.cr1().write(|w| { // enable uart w.set_ue(true); From fda36fd81b25bac914e977654beecdb41aaabbb3 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Mon, 6 Feb 2023 10:12:10 +1100 Subject: [PATCH 326/374] stm32/usart: fix LPUART clock multiplier According to RM0351 Rev 9 (L4) and RM0399 Rev 3 (H7): baud = (256 * clock) / LPUARTDIV --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 20f4eede..121699c7 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1148,7 +1148,7 @@ macro_rules! impl_lpuart { foreach_interrupt!( ($inst:ident, lpuart, $block:ident, $signal_name:ident, $irq:ident) => { - impl_lpuart!($inst, $irq, 255); + impl_lpuart!($inst, $irq, 256); }; ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { From e3174d7a998ead0fbe5ddadf59914cc16a561a44 Mon Sep 17 00:00:00 2001 From: Ralf Date: Sun, 5 Feb 2023 20:17:53 +0100 Subject: [PATCH 327/374] STM32 SPI: Set clk-pin pull-up/-down to match spi clock polarity RM0394: 40.4.6 Communication formats ... The idle state of SCK must correspond to the polarity selected in the SPIx_CR1 register (by pulling up SCK if CPOL=1 or pulling down SCK if CPOL=0). --- embassy-stm32/src/spi/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 5b81c791..e5ba746e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -10,7 +10,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use self::sealed::WordSize; use crate::dma::{slice_ptr_parts, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, Pull}; use crate::pac::spi::{regs, vals, Spi as Regs}; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -93,8 +93,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { config: Config, ) -> Self { into_ref!(peri, sck, mosi, miso); + + let sck_pull_mode = match config.mode.polarity { + Polarity::IdleLow => Pull::Down, + Polarity::IdleHigh => Pull::Up, + }; + unsafe { - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode); sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); mosi.set_speed(crate::gpio::Speed::VeryHigh); From 218f8e049096341c459de8d39146d59117d20d69 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 12:17:37 +0100 Subject: [PATCH 328/374] fix(stm32): Align FMC with new versions from stm32-data --- embassy-stm32/src/fmc/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 856a4adc..0c546104 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -27,9 +27,13 @@ where } fn memory_controller_enable(&mut self) { - // The FMCEN bit of the FMC_BCR2..4 registers is don’t - // care. It is only enabled through the FMC_BCR1 register. - unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) }; + // fmc v1 and v2 does not have the fmcen bit + // fsmc v1 and v2 does not have the fmcen bit + // This is a "not" because it is expected that all future versions have this bit + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x3, fsmc_v2x1)))] + unsafe { + T::regs().bcr1().modify(|r| r.set_fmcen(true)) + }; } fn source_clock_hz(&self) -> u32 { From 36ca18132dd525bc2a3cdac0246834a2aae07ca9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 12:35:59 +0100 Subject: [PATCH 329/374] Update stm32-data --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index 96decdd6..fe4a068e 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 96decdd6114d78813c1f748fb878a45e1b03bf73 +Subproject commit fe4a068eaf1b39c60eddea7d74c90182b44c5d08 From 494a76a0f1be2fb35000c7a088f9bda21c73ad9e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 14:14:47 +0100 Subject: [PATCH 330/374] React to updated fsmc versions --- embassy-stm32/src/fmc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 0c546104..13c16af5 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -28,9 +28,9 @@ where fn memory_controller_enable(&mut self) { // fmc v1 and v2 does not have the fmcen bit - // fsmc v1 and v2 does not have the fmcen bit + // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit - #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x3, fsmc_v2x1)))] + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v2x3, fsmc_v3x1)))] unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) }; From 562432ad8b9380cff7a536105b9877cdd8044870 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 14:16:13 +0100 Subject: [PATCH 331/374] Update stm32-data --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index fe4a068e..ae8c8ed9 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit fe4a068eaf1b39c60eddea7d74c90182b44c5d08 +Subproject commit ae8c8ed9dbde5eca7c5c74bd926f7ce77526dae1 From e4dc473e04ba2981cffd16ce4348f0646cae341d Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 14:46:36 +0100 Subject: [PATCH 332/374] Update stm32-data --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index ae8c8ed9..cc93f9d1 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit ae8c8ed9dbde5eca7c5c74bd926f7ce77526dae1 +Subproject commit cc93f9d10395077770bebefb6b9488e06b0e5811 From 1b6aae9ddea09bf961d9cbe4a976f3021749e4ac Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 15:06:16 +0100 Subject: [PATCH 333/374] Also exclude fsmc_v1x3 --- embassy-stm32/src/fmc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 13c16af5..4d48721d 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -30,7 +30,7 @@ where // fmc v1 and v2 does not have the fmcen bit // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit - #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v2x3, fsmc_v3x1)))] + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))] unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) }; From 7b11e339bd9ce69f114778e1401cbe8279a70337 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 16:06:59 +0100 Subject: [PATCH 334/374] feat(fmc): Add 16 data bit ctor --- embassy-stm32/src/fmc/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 856a4adc..a781e437 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -107,6 +107,24 @@ impl<'d, T: Instance> Fmc<'d, T> { ] )); + fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank2: ( + bank: stm32_fmc::SdramTargetBank::Bank2, + addr: [ + (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin) + ], + ba: [(ba0: BA0Pin), (ba1: BA1Pin)], + d: [ + (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), + (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) + ], + nbl: [ + (nbl0: NBL0Pin), (nbl1: NBL1Pin) + ], + ctrl: [ + (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) + ] + )); + fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank2: ( bank: stm32_fmc::SdramTargetBank::Bank2, addr: [ From 102b2e52cb0b98a533ec12a1c0b93d8dc9e4f826 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 18:15:26 +0100 Subject: [PATCH 335/374] net: use released smoltcp 0.9.0 --- embassy-net/Cargo.toml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 15cbb595..4ec340b7 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -33,6 +33,12 @@ medium-ip = ["smoltcp/medium-ip"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +smoltcp = { version = "0.9.0", default-features = false, features = [ + "proto-ipv4", + "socket", + "async", +]} + embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } @@ -47,14 +53,3 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw atomic-pool = "1.0" embedded-nal-async = { version = "0.3.0", optional = true } atomic-polyfill = { version = "1.0" } - -[dependencies.smoltcp] -version = "0.8.0" -git = "https://github.com/smoltcp-rs/smoltcp" -rev = "5740b765749b95c18aace5de8dc21cab75ba33d4" -default-features = false -features = [ - "proto-ipv4", - "socket", - "async", -] From f5ff3c4ac31c79cedf077f559dbd5685886399cc Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Thu, 12 Jan 2023 14:59:25 -0600 Subject: [PATCH 336/374] usb: add support for MS OS Descriptors --- embassy-usb/Cargo.toml | 1 + embassy-usb/src/builder.rs | 15 + embassy-usb/src/lib.rs | 17 + embassy-usb/src/msos.rs | 746 +++++++++++++++++++++++++++++++++++++ 4 files changed, 779 insertions(+) create mode 100644 embassy-usb/src/msos.rs diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1e567bb9..31d1f4ca 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -24,6 +24,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" +widestring = { version = "1.0.2", default-features = false } # for HID usbd-hid = { version = "0.6.0", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 41b24fec..2c42fd64 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -130,6 +130,7 @@ pub struct Builder<'d, D: Driver<'d>> { device_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, + msos_descriptor: Option>, } impl<'d, D: Driver<'d>> Builder<'d, D> { @@ -182,6 +183,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor, config_descriptor, bos_descriptor, + msos_descriptor: None, } } @@ -199,6 +201,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.writer.into_buf(), self.interfaces, self.control_buf, + self.msos_descriptor, ) } @@ -234,6 +237,18 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { iface_count_index, } } + + /// Add an MS OS 2.0 Descriptor Set. + /// + /// Panics if called more than once. + pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { + if self.msos_descriptor.is_some() { + panic!("msos_descriptor already set"); + } + self.msos_descriptor + .insert(msos_descriptor) + .write_bos_capability(&mut self.bos_descriptor); + } } /// Function builder. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 2656af29..948b8d52 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -13,6 +13,7 @@ pub mod class; pub mod control; pub mod descriptor; mod descriptor_reader; +pub mod msos; pub mod types; use embassy_futures::select::{select, Either}; @@ -135,6 +136,8 @@ struct Inner<'d, D: Driver<'d>> { set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, + + msos_descriptor: Option>, } impl<'d, D: Driver<'d>> UsbDevice<'d, D> { @@ -147,6 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { bos_descriptor: &'d [u8], interfaces: Vec, MAX_INTERFACE_COUNT>, control_buf: &'d mut [u8], + msos_descriptor: Option>, ) -> UsbDevice<'d, D> { // Start the USB bus. // This prevent further allocation by consuming the driver. @@ -170,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + msos_descriptor, }, } } @@ -603,6 +608,18 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { None => InResponse::Rejected, } } + (RequestType::Vendor, Recipient::Device) => { + if let Some(msos) = &self.msos_descriptor { + if req.request == msos.vendor_code() && req.index == 7 { + // Index 7 retrieves the MS OS Descriptor Set + InResponse::Accepted(msos.descriptor()) + } else { + InResponse::Rejected + } + } else { + InResponse::Rejected + } + } _ => InResponse::Rejected, } } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs new file mode 100644 index 00000000..08a5074b --- /dev/null +++ b/embassy-usb/src/msos.rs @@ -0,0 +1,746 @@ +//! Microsoft OS Descriptors +//! +//! + +#![allow(dead_code)] + +use core::mem::size_of; +use core::ops::Range; + +pub use widestring::{u16cstr, U16CStr}; + +use crate::descriptor::{capability_type, BosWriter}; +use crate::types::InterfaceNumber; + +fn write_u16>(buf: &mut [u8], range: Range, data: T) { + (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) +} + +/// A serialized Microsoft OS 2.0 Descriptor set. +/// +/// Create with [`DeviceDescriptorSetBuilder`]. +pub struct MsOsDescriptorSet<'a> { + descriptor: &'a [u8], + windows_version: u32, + vendor_code: u8, +} + +impl<'a> MsOsDescriptorSet<'a> { + pub fn descriptor(&self) -> &[u8] { + self.descriptor + } + + pub fn vendor_code(&self) -> u8 { + self.vendor_code + } + + pub fn write_bos_capability(&self, bos: &mut BosWriter) { + let windows_version = self.windows_version.to_le_bytes(); + let len = self.descriptor.len().to_le_bytes(); + bos.capability( + capability_type::PLATFORM, + &[ + 0, // reserved + // platform capability UUID, Microsoft OS 2.0 platform compabitility + 0xdf, + 0x60, + 0xdd, + 0xd8, + 0x89, + 0x45, + 0xc7, + 0x4c, + 0x9c, + 0xd2, + 0x65, + 0x9d, + 0x9e, + 0x64, + 0x8a, + 0x9f, + // Minimum compatible Windows version + windows_version[0], + windows_version[1], + windows_version[2], + windows_version[3], + // Descriptor set length + len[0], + len[1], + self.vendor_code, + 0x0, // Device does not support alternate enumeration + ], + ) + } +} + +/// A helper struct to implement the different descriptor set builders. +struct DescriptorSetBuilder<'a> { + used: usize, + buf: &'a mut [u8], +} + +impl<'a> DescriptorSetBuilder<'a> { + pub fn descriptor(&mut self, desc: T) + where + T: Descriptor + 'a, + { + let size = desc.size(); + let start = self.used; + let end = start + size; + desc.write_to(&mut self.buf[start..end]); + self.used += size; + } + + pub fn subset(&mut self, build_subset: impl FnOnce(&mut DescriptorSetBuilder<'_>)) { + self.used += { + let mut subset = DescriptorSetBuilder { + used: 0, + buf: self.remaining(), + }; + build_subset(&mut subset); + subset.used + }; + } + + pub fn remaining(&mut self) -> &mut [u8] { + &mut self.buf[self.used..] + } +} + +pub mod windows_version { + pub const WIN2K: u32 = 0x05000000; + pub const WIN2KSP1: u32 = 0x05000100; + pub const WIN2KSP2: u32 = 0x05000200; + pub const WIN2KSP3: u32 = 0x05000300; + pub const WIN2KSP4: u32 = 0x05000400; + + pub const WINXP: u32 = 0x05010000; + pub const WINXPSP1: u32 = 0x05010100; + pub const WINXPSP2: u32 = 0x05010200; + pub const WINXPSP3: u32 = 0x05010300; + pub const WINXPSP4: u32 = 0x05010400; + + pub const VISTA: u32 = 0x06000000; + pub const VISTASP1: u32 = 0x06000100; + pub const VISTASP2: u32 = 0x06000200; + pub const VISTASP3: u32 = 0x06000300; + pub const VISTASP4: u32 = 0x06000400; + + pub const WIN7: u32 = 0x06010000; + pub const WIN8: u32 = 0x06020000; + /// AKA `NTDDI_WINBLUE` + pub const WIN8_1: u32 = 0x06030000; + pub const WIN10: u32 = 0x0A000000; +} + +/// Helps build a Microsoft OS 2.0 Descriptor set. +/// +/// # Example +/// ```rust +/// # use embassy_usb::types::InterfaceNumber; +/// # use embassy_usb::msos::*; +/// # let cdc_interface = unsafe { core::mem::transmute::(0) }; +/// # let dfu_interface = unsafe { core::mem::transmute::(1) }; +/// let mut buf = [0u8; 256]; +/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1); +/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10)); +/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128)); +/// builder.configuration(1, |conf| { +/// conf.function(cdc_interface, |func| { +/// func.winusb_device(); +/// func.feature(VendorRevisionDescriptor::new(1)); +/// }); +/// conf.function(dfu_interface, |func| { +/// func.winusb_device(); +/// func.feature(VendorRevisionDescriptor::new(1)); +/// }); +/// }); +/// ``` +pub struct DeviceDescriptorSetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, + windows_version: u32, + vendor_code: u8, +} + +impl<'a> DeviceDescriptorSetBuilder<'a> { + /// Create a device descriptor set builder. + /// + /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] + /// module. + /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. + pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(DescriptorSetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(), + dwWindowsVersion: windows_version.to_le(), + wTotalLength: 0, + }); + Self { + builder, + windows_version, + vendor_code, + } + } + + /// Add a device-level feature descriptor. + /// + /// Note that some feature descriptors may only be used at the device level in non-composite devices. + pub fn feature(&mut self, desc: T) + where + T: Descriptor + DeviceLevelDescriptor + 'a, + { + self.builder.descriptor(desc) + } + + /// Add a configuration subset. + pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) { + let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration); + build_conf(&mut cb); + self.builder.used += cb.finalize(); + } + + /// Finishes writing the data. + pub fn finalize(self) -> MsOsDescriptorSet<'a> { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[4..6]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + + MsOsDescriptorSet { + descriptor: &buf[..used], + windows_version: self.windows_version, + vendor_code: self.vendor_code, + } + } +} + +pub struct ConfigurationSubsetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, +} + +impl<'a> ConfigurationSubsetBuilder<'a> { + pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(ConfigurationSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(), + bConfigurationValue: configuration, + bReserved: 0, + wTotalLength: 0, + }); + Self { builder } + } + + /// Add a function subset. + pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) { + let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface); + build_func(&mut fb); + self.builder.used += fb.finalize(); + } + + /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. + pub fn finalize(self) -> usize { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[6..8]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + used + } +} + +pub struct FunctionSubsetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, +} + +impl<'a> FunctionSubsetBuilder<'a> { + pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(FunctionSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(), + bFirstInterface: interface.0, + bReserved: 0, + wSubsetLength: 0, + }); + Self { builder } + } + + /// Add a function-level descriptor. + /// + /// Note that many descriptors can only be used at function-level in a composite device. + pub fn feature(&mut self, desc: T) + where + T: Descriptor + FunctionLevelDescriptor + 'a, + { + self.builder.descriptor(desc) + } + + /// Adds the feature descriptors to configure this function to use the WinUSB driver. + /// + /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the + /// USB_DEVICE GUID. + pub fn winusb_device(&mut self) { + self.feature(CompatibleIdFeatureDescriptor::new_winusb()); + self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid()); + } + + /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. + pub fn finalize(self) -> usize { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[6..8]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + used + } +} + +/// A trait for descriptors +pub trait Descriptor: Sized { + const TYPE: DescriptorType; + + /// The size of the descriptor's header. + fn size(&self) -> usize { + size_of::() + } + + fn write_to(&self, buf: &mut [u8]); +} + +/// Copies the data of `t` into `buf`. +/// +/// # Safety +/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) +unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { + let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); + assert!(buf.len() >= bytes.len()); + (&mut buf[..bytes.len()]).copy_from_slice(bytes); +} + +/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum DescriptorType { + SetHeaderDescriptor = 0, + SubsetHeaderConfiguration = 1, + SubsetHeaderFunction = 2, + FeatureCompatibleId = 3, + FeatureRegProperty = 4, + FeatureMinResumeTime = 5, + FeatureModelId = 6, + FeatureCcgpDevice = 7, + FeatureVendorRevision = 8, +} + +/// Table 5. Descriptor set information structure. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct DescriptorSetInformation { + dwWindowsVersion: u32, + wMSOSDescriptorSetTotalLength: u16, + bMS_VendorCode: u8, + bAltEnumCode: u8, +} + +/// Table 4. Microsoft OS 2.0 platform capability descriptor header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct PlatformDescriptor { + bLength: u8, + bDescriptorType: u8, + bDevCapabilityType: u8, + bReserved: u8, + platformCapabilityUUID: [u8; 16], + descriptor_set_information: DescriptorSetInformation, +} + +/// Table 10. Microsoft OS 2.0 descriptor set header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct DescriptorSetHeader { + wLength: u16, + wDescriptorType: u16, + dwWindowsVersion: u32, + wTotalLength: u16, +} + +impl Descriptor for DescriptorSetHeader { + const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +/// Table 11. Configuration subset header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct ConfigurationSubsetHeader { + wLength: u16, + wDescriptorType: u16, + bConfigurationValue: u8, + bReserved: u8, + wTotalLength: u16, +} + +impl Descriptor for ConfigurationSubsetHeader { + const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +/// Table 12. Function subset header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct FunctionSubsetHeader { + wLength: u16, + wDescriptorType: u16, + bFirstInterface: u8, + bReserved: u8, + wSubsetLength: u16, +} + +impl Descriptor for FunctionSubsetHeader { + const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +// Feature Descriptors + +/// A marker trait for feature descriptors that are valid at the device level. +pub trait DeviceLevelDescriptor {} + +/// A marker trait for feature descriptors that are valid at the function level. +pub trait FunctionLevelDescriptor { + /// `true` when the feature descriptor may only be used at the function level in composite devices. + const COMPOSITE_ONLY: bool = false; +} + +/// Table 13. Microsoft OS 2.0 compatible ID descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct CompatibleIdFeatureDescriptor { + wLength: u16, + wDescriptorType: u16, + compatibleId: [u8; 8], + subCompatibleId: [u8; 8], +} + +impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} +impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { + const COMPOSITE_ONLY: bool = true; +} + +impl Descriptor for CompatibleIdFeatureDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl CompatibleIdFeatureDescriptor { + /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty. + pub fn new_winusb() -> Self { + Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8]) + } + + /// The ids must be 8 ASCII bytes or fewer. + pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { + assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); + let mut cid = [0u8; 8]; + (&mut cid[..compatible_id.len()]).copy_from_slice(compatible_id.as_bytes()); + let mut scid = [0u8; 8]; + (&mut scid[..sub_compatible_id.len()]).copy_from_slice(sub_compatible_id.as_bytes()); + Self::new_raw(cid, scid) + } + + pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + compatibleId: compatible_id, + subCompatibleId: sub_compatible_id, + } + } +} + +/// Table 14. Microsoft OS 2.0 registry property descriptor +#[allow(non_snake_case)] +pub struct RegistryPropertyFeatureDescriptor<'a> { + wLength: u16, + wDescriptorType: u16, + wPropertyDataType: u16, + wPropertyNameLength: u16, + PropertyName: &'a [u8], + wPropertyDataLength: u16, + PropertyData: &'a [u8], +} + +impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { + const COMPOSITE_ONLY: bool = true; +} + +impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { + const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; + fn size(&self) -> usize { + 10 + self.PropertyName.len() + self.PropertyData.len() + } + fn write_to(&self, buf: &mut [u8]) { + assert!(buf.len() >= self.size()); + assert!(self.wPropertyNameLength as usize == self.PropertyName.len()); + assert!(self.wPropertyDataLength as usize == self.PropertyData.len()); + write_u16(buf, 0..2, self.wLength); + write_u16(buf, 2..4, self.wDescriptorType); + write_u16(buf, 4..6, self.wPropertyDataType); + write_u16(buf, 6..8, self.wPropertyNameLength); + let pne = 8 + self.PropertyName.len(); + (&mut buf[8..pne]).copy_from_slice(self.PropertyName); + let pds = pne + 2; + let pde = pds + self.PropertyData.len(); + write_u16(buf, pne..pds, self.wPropertyDataLength); + (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); + } +} + +impl<'a> RegistryPropertyFeatureDescriptor<'a> { + /// A registry property. + /// + /// `name` should be a NUL-terminated 16-bit Unicode string. + pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { + Self { + wLength: ((10 + name.len() + data.len()) as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + wPropertyDataType: (data_type as u16).to_le(), + wPropertyNameLength: (name.len() as u16).to_le(), + PropertyName: name, + wPropertyDataLength: (data.len() as u16).to_le(), + PropertyData: data, + } + } + + fn u16str_bytes(s: &U16CStr) -> &[u8] { + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } + } + + /// A registry property that sets the DeviceInterfaceGUID to the device interface class for USB devices which are + /// attached to a USB hub. + pub fn new_usb_deviceinterfaceguid() -> Self { + // Can't use defmt::panic in constant expressions (inside u16cstr!) + macro_rules! panic { + ($($x:tt)*) => { + { + ::core::panic!($($x)*); + } + }; + } + + Self::new_string( + u16cstr!("DeviceInterfaceGUID"), + u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"), + ) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string. + pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables. + pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + Self::u16str_bytes(data), + PropertyDataType::ExpandSz, + ) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link. + pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + Self::u16str_bytes(data), + PropertyDataType::Link, + ) + } + + /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. + pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, + PropertyDataType::RegMultiSz, + ) + } + + /// A registry property containing binary data. + pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self { + Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary) + } + + /// A registry property containing a Little-Endian 32-bit integer. + /// + /// The function assumes that `data` is already little-endian, it does not convert it. + pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, + PropertyDataType::DwordLittleEndian, + ) + } + + /// A registry property containing a big-endian 32-bit integer. + /// + /// The function assumes that `data` is already big-endian, it does not convert it. + pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, + PropertyDataType::DwordBigEndian, + ) + } +} + +/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum PropertyDataType { + Sz = 1, + ExpandSz = 2, + Binary = 3, + DwordLittleEndian = 4, + DwordBigEndian = 5, + Link = 6, + RegMultiSz = 7, +} + +/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct MinimumRecoveryTimeDescriptor { + wLength: u16, + wDescriptorType: u16, + bResumeRecoveryTime: u8, + bResumeSignalingTime: u8, +} + +impl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {} + +impl Descriptor for MinimumRecoveryTimeDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl MinimumRecoveryTimeDescriptor { + /// Times are in milliseconds. + /// + /// `resume_recovery_time` must be >= 0 and <= 10. + /// `resume_signaling_time` must be >= 1 and <= 20. + pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self { + assert!(resume_recovery_time <= 10); + assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20); + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bResumeRecoveryTime: resume_recovery_time, + bResumeSignalingTime: resume_signaling_time, + } + } +} + +/// Table 17. Microsoft OS 2.0 model ID descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct ModelIdDescriptor { + wLength: u16, + wDescriptorType: u16, + modelId: [u8; 16], +} + +impl DeviceLevelDescriptor for ModelIdDescriptor {} + +impl Descriptor for ModelIdDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureModelId; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl ModelIdDescriptor { + pub fn new(model_id: u128) -> Self { + Self::new_bytes(model_id.to_le_bytes()) + } + + pub fn new_bytes(model_id: [u8; 16]) -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + modelId: model_id, + } + } +} + +/// Table 18. Microsoft OS 2.0 CCGP device descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct CcgpDeviceDescriptor { + wLength: u16, + wDescriptorType: u16, +} + +impl DeviceLevelDescriptor for CcgpDeviceDescriptor {} + +impl Descriptor for CcgpDeviceDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl CcgpDeviceDescriptor { + pub fn new() -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + } + } +} + +/// Table 19. Microsoft OS 2.0 vendor revision descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct VendorRevisionDescriptor { + wLength: u16, + wDescriptorType: u16, + /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or + /// other MSOS descriptor. Shell set to greater than or equal to 1. + VendorRevision: u16, +} + +impl DeviceLevelDescriptor for VendorRevisionDescriptor {} +impl FunctionLevelDescriptor for VendorRevisionDescriptor {} + +impl Descriptor for VendorRevisionDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl VendorRevisionDescriptor { + pub fn new(revision: u16) -> Self { + assert!(revision >= 1); + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + VendorRevision: revision.to_le(), + } + } +} From 617b0a03b94d8efde3e89bd80b67caf9a0d4a35d Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 13 Jan 2023 10:18:33 -0600 Subject: [PATCH 337/374] usb: fix descriptor set length and DeviceInterfaceGUIDs --- embassy-usb/src/msos.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 08a5074b..8a2889ce 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -205,7 +205,7 @@ impl<'a> DeviceDescriptorSetBuilder<'a> { let used = self.builder.used; let buf = self.builder.buf; // Update length in header with final length - let total_len = &mut buf[4..6]; + let total_len = &mut buf[8..10]; total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); MsOsDescriptorSet { @@ -528,7 +528,7 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } } - /// A registry property that sets the DeviceInterfaceGUID to the device interface class for USB devices which are + /// A registry property that sets the DeviceInterfaceGUIDs to the device interface class for USB devices which are /// attached to a USB hub. pub fn new_usb_deviceinterfaceguid() -> Self { // Can't use defmt::panic in constant expressions (inside u16cstr!) @@ -540,9 +540,9 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { }; } - Self::new_string( - u16cstr!("DeviceInterfaceGUID"), - u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"), + Self::new_multi_string( + u16cstr!("DeviceInterfaceGUIDs"), + u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}").as_slice_with_nul(), ) } From b9ecdb72bb55792a8fa5a0bace8cdad498fee9b0 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 13 Jan 2023 12:21:29 -0600 Subject: [PATCH 338/374] usb: remove msos dead code --- embassy-usb/src/msos.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 8a2889ce..df846125 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -2,8 +2,6 @@ //! //! -#![allow(dead_code)] - use core::mem::size_of; use core::ops::Range; @@ -91,17 +89,6 @@ impl<'a> DescriptorSetBuilder<'a> { self.used += size; } - pub fn subset(&mut self, build_subset: impl FnOnce(&mut DescriptorSetBuilder<'_>)) { - self.used += { - let mut subset = DescriptorSetBuilder { - used: 0, - buf: self.remaining(), - }; - build_subset(&mut subset); - subset.used - }; - } - pub fn remaining(&mut self) -> &mut [u8] { &mut self.buf[self.used..] } From 9f9230ae7abb545822e59c6f06cabb721b63e0a1 Mon Sep 17 00:00:00 2001 From: alexmoon Date: Thu, 2 Feb 2023 16:13:16 -0500 Subject: [PATCH 339/374] Convert MS OS descriptor builder to a writer API This brings it inline with the other embassy-usb descriptor APIs and allows it to integrate well with the Builder to allow class constructors to add MS OS descriptors. Also adds a `usb_serial_winusb` example to demonstrate how to use the API. --- embassy-usb/Cargo.toml | 3 +- embassy-usb/src/builder.rs | 78 ++- embassy-usb/src/lib.rs | 14 +- embassy-usb/src/msos.rs | 493 +++++++++--------- examples/nrf52840/Cargo.toml | 7 +- .../nrf52840/src/bin/usb_serial_winusb.rs | 139 +++++ 6 files changed, 461 insertions(+), 273 deletions(-) create mode 100644 examples/nrf52840/src/bin/usb_serial_winusb.rs diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 31d1f4ca..54a8f27c 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -13,6 +13,7 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] +msos-descriptor = ["dep:widestring"] default = ["usbd-hid"] [dependencies] @@ -24,7 +25,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" -widestring = { version = "1.0.2", default-features = false } +widestring = { version = "1.0.2", default-features = false, optional = true } # for HID usbd-hid = { version = "0.6.0", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 2c42fd64..d1cbf674 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -3,6 +3,8 @@ use heapless::Vec; use crate::control::ControlHandler; use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::driver::{Driver, Endpoint, EndpointType}; +#[cfg(feature = "msos-descriptor")] +use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::*; use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; @@ -130,7 +132,9 @@ pub struct Builder<'d, D: Driver<'d>> { device_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, - msos_descriptor: Option>, + + #[cfg(feature = "msos-descriptor")] + msos_descriptor: MsOsDescriptorWriter<'d>, } impl<'d, D: Driver<'d>> Builder<'d, D> { @@ -145,6 +149,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor_buf: &'d mut [u8], config_descriptor_buf: &'d mut [u8], bos_descriptor_buf: &'d mut [u8], + #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], control_buf: &'d mut [u8], handler: Option<&'d dyn DeviceStateHandler>, ) -> Self { @@ -183,12 +188,17 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor, config_descriptor, bos_descriptor, - msos_descriptor: None, + + #[cfg(feature = "msos-descriptor")] + msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf), } } /// Creates the [`UsbDevice`] instance with the configuration in this builder. pub fn build(mut self) -> UsbDevice<'d, D> { + #[cfg(feature = "msos-descriptor")] + let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor); + self.config_descriptor.end_configuration(); self.bos_descriptor.end_bos(); @@ -201,7 +211,8 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.writer.into_buf(), self.interfaces, self.control_buf, - self.msos_descriptor, + #[cfg(feature = "msos-descriptor")] + msos_descriptor, ) } @@ -218,14 +229,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { /// /// If it's not set, no IAD descriptor is added. pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> { + let first_interface = InterfaceNumber::new(self.interfaces.len() as u8); let iface_count_index = if self.config.composite_with_iads { - self.config_descriptor.iad( - InterfaceNumber::new(self.interfaces.len() as _), - 0, - class, - subclass, - protocol, - ); + self.config_descriptor + .iad(first_interface, 0, class, subclass, protocol); Some(self.config_descriptor.position() - 5) } else { @@ -235,19 +242,31 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { FunctionBuilder { builder: self, iface_count_index, + + #[cfg(feature = "msos-descriptor")] + first_interface, } } + #[cfg(feature = "msos-descriptor")] /// Add an MS OS 2.0 Descriptor Set. /// /// Panics if called more than once. - pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { - if self.msos_descriptor.is_some() { - panic!("msos_descriptor already set"); - } - self.msos_descriptor - .insert(msos_descriptor) - .write_bos_capability(&mut self.bos_descriptor); + pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) { + self.msos_descriptor.header(windows_version, vendor_code); + } + + #[cfg(feature = "msos-descriptor")] + /// Add an MS OS 2.0 Device Level Feature Descriptor. + pub fn msos_feature(&mut self, desc: T) { + self.msos_descriptor.device_feature(desc); + } + + #[cfg(feature = "msos-descriptor")] + /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that + /// do not add their own. + pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> { + &mut self.msos_descriptor } } @@ -259,6 +278,16 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> { builder: &'a mut Builder<'d, D>, iface_count_index: Option, + + #[cfg(feature = "msos-descriptor")] + first_interface: InterfaceNumber, +} + +impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> { + fn drop(&mut self) { + #[cfg(feature = "msos-descriptor")] + self.builder.msos_descriptor.end_function(); + } } impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { @@ -288,6 +317,21 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { next_alt_setting_number: 0, } } + + #[cfg(feature = "msos-descriptor")] + /// Add an MS OS 2.0 Function Level Feature Descriptor. + pub fn msos_feature(&mut self, desc: T) { + if !self.builder.msos_descriptor.is_in_config_subset() { + self.builder.msos_descriptor.configuration(0); + } + + if !self.builder.msos_descriptor.is_in_function_subset() { + self.builder.msos_descriptor.function(self.first_interface.0); + } + + #[cfg(feature = "msos-descriptor")] + self.builder.msos_descriptor.function_feature(desc); + } } /// Interface builder. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 948b8d52..aec18524 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -136,8 +136,8 @@ struct Inner<'d, D: Driver<'d>> { set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, - - msos_descriptor: Option>, + #[cfg(feature = "msos-descriptor")] + msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, } impl<'d, D: Driver<'d>> UsbDevice<'d, D> { @@ -150,7 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { bos_descriptor: &'d [u8], interfaces: Vec, MAX_INTERFACE_COUNT>, control_buf: &'d mut [u8], - msos_descriptor: Option>, + #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, ) -> UsbDevice<'d, D> { // Start the USB bus. // This prevent further allocation by consuming the driver. @@ -174,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + #[cfg(feature = "msos-descriptor")] msos_descriptor, }, } @@ -608,11 +609,12 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { None => InResponse::Rejected, } } + #[cfg(feature = "msos-descriptor")] (RequestType::Vendor, Recipient::Device) => { - if let Some(msos) = &self.msos_descriptor { - if req.request == msos.vendor_code() && req.index == 7 { + if !self.msos_descriptor.is_empty() { + if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { // Index 7 retrieves the MS OS Descriptor Set - InResponse::Accepted(msos.descriptor()) + InResponse::Accepted(self.msos_descriptor.descriptor()) } else { InResponse::Rejected } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index df846125..360f80d9 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "msos-descriptor")] + //! Microsoft OS Descriptors //! //! @@ -5,10 +7,9 @@ use core::mem::size_of; use core::ops::Range; -pub use widestring::{u16cstr, U16CStr}; +pub use widestring::{u16cstr, u16str, U16CStr, U16Str}; -use crate::descriptor::{capability_type, BosWriter}; -use crate::types::InterfaceNumber; +use super::{capability_type, BosWriter}; fn write_u16>(buf: &mut [u8], range: Range, data: T) { (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) @@ -17,13 +18,12 @@ fn write_u16>(buf: &mut [u8], range: Range, data: T) { /// A serialized Microsoft OS 2.0 Descriptor set. /// /// Create with [`DeviceDescriptorSetBuilder`]. -pub struct MsOsDescriptorSet<'a> { - descriptor: &'a [u8], - windows_version: u32, +pub struct MsOsDescriptorSet<'d> { + descriptor: &'d [u8], vendor_code: u8, } -impl<'a> MsOsDescriptorSet<'a> { +impl<'d> MsOsDescriptorSet<'d> { pub fn descriptor(&self) -> &[u8] { self.descriptor } @@ -32,9 +32,150 @@ impl<'a> MsOsDescriptorSet<'a> { self.vendor_code } - pub fn write_bos_capability(&self, bos: &mut BosWriter) { - let windows_version = self.windows_version.to_le_bytes(); - let len = self.descriptor.len().to_le_bytes(); + pub fn is_empty(&self) -> bool { + self.descriptor.is_empty() + } +} + +/// Writes a Microsoft OS 2.0 Descriptor set into a buffer. +pub struct MsOsDescriptorWriter<'d> { + pub buf: &'d mut [u8], + + position: usize, + config_mark: Option, + function_mark: Option, + vendor_code: u8, +} + +impl<'d> MsOsDescriptorWriter<'d> { + pub(crate) fn new(buf: &'d mut [u8]) -> Self { + MsOsDescriptorWriter { + buf, + position: 0, + config_mark: None, + function_mark: None, + vendor_code: 0, + } + } + + pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> { + self.end(); + + if self.is_empty() { + MsOsDescriptorSet { + descriptor: &[], + vendor_code: 0, + } + } else { + self.write_bos(bos); + MsOsDescriptorSet { + descriptor: &self.buf[..self.position], + vendor_code: self.vendor_code, + } + } + } + + pub fn is_empty(&self) -> bool { + self.position == 0 + } + + pub fn is_in_config_subset(&self) -> bool { + self.config_mark.is_some() + } + + pub fn is_in_function_subset(&self) -> bool { + self.function_mark.is_some() + } + + /// Write the MS OS descriptor set header. + /// + /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] + /// module. + /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. + pub fn header(&mut self, windows_version: u32, vendor_code: u8) { + assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once"); + self.write(DescriptorSetHeader::new(windows_version)); + self.vendor_code = vendor_code; + } + + /// Add a device level feature descriptor. + /// + /// Note that some feature descriptors may only be used at the device level in non-composite devices. + /// Those features must be written before the first call to [`Self::configuration`]. + pub fn device_feature(&mut self, desc: T) { + assert!( + !self.is_empty(), + "device features may only be added after the header is written" + ); + assert!( + self.config_mark.is_none(), + "device features must be added before the first configuration subset" + ); + self.write(desc); + } + + /// Add a configuration subset. + pub fn configuration(&mut self, config: u8) { + assert!( + !self.is_empty(), + "MsOsDescriptorWriter: configuration must be called after header" + ); + Self::end_subset::(self.buf, self.position, &mut self.config_mark); + self.config_mark = Some(self.position); + self.write(ConfigurationSubsetHeader::new(config)); + } + + /// Add a function subset. + pub fn function(&mut self, first_interface: u8) { + assert!( + self.config_mark.is_some(), + "MsOsDescriptorWriter: function subset requires a configuration subset" + ); + self.end_function(); + self.function_mark = Some(self.position); + self.write(FunctionSubsetHeader::new(first_interface)); + } + + /// Add a function level feature descriptor. + /// + /// Note that some features may only be used at the function level. Those features must be written after a call + /// to [`Self::function`]. + pub fn function_feature(&mut self, desc: T) { + assert!( + self.function_mark.is_some(), + "function features may only be added to a function subset" + ); + self.write(desc); + } + + pub fn end_function(&mut self) { + Self::end_subset::(self.buf, self.position, &mut self.function_mark); + } + + fn write(&mut self, desc: T) { + desc.write_to(&mut self.buf[self.position..]); + self.position += desc.size(); + } + + fn end_subset(buf: &mut [u8], position: usize, mark: &mut Option) { + if let Some(mark) = mark.take() { + let len = position - mark; + let p = mark + T::LENGTH_OFFSET; + buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes()); + } + } + + fn end(&mut self) { + if self.position > 0 { + Self::end_subset::(self.buf, self.position, &mut self.function_mark); + Self::end_subset::(self.buf, self.position, &mut self.config_mark); + Self::end_subset::(self.buf, self.position, &mut Some(0)); + } + } + + fn write_bos(&mut self, bos: &mut BosWriter) { + let windows_version = &self.buf[4..8]; + let len = (self.position as u16).to_le_bytes(); bos.capability( capability_type::PLATFORM, &[ @@ -67,30 +208,7 @@ impl<'a> MsOsDescriptorSet<'a> { self.vendor_code, 0x0, // Device does not support alternate enumeration ], - ) - } -} - -/// A helper struct to implement the different descriptor set builders. -struct DescriptorSetBuilder<'a> { - used: usize, - buf: &'a mut [u8], -} - -impl<'a> DescriptorSetBuilder<'a> { - pub fn descriptor(&mut self, desc: T) - where - T: Descriptor + 'a, - { - let size = desc.size(); - let start = self.used; - let end = start + size; - desc.write_to(&mut self.buf[start..end]); - self.used += size; - } - - pub fn remaining(&mut self) -> &mut [u8] { - &mut self.buf[self.used..] + ); } } @@ -120,182 +238,27 @@ pub mod windows_version { pub const WIN10: u32 = 0x0A000000; } -/// Helps build a Microsoft OS 2.0 Descriptor set. -/// -/// # Example -/// ```rust -/// # use embassy_usb::types::InterfaceNumber; -/// # use embassy_usb::msos::*; -/// # let cdc_interface = unsafe { core::mem::transmute::(0) }; -/// # let dfu_interface = unsafe { core::mem::transmute::(1) }; -/// let mut buf = [0u8; 256]; -/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1); -/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10)); -/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128)); -/// builder.configuration(1, |conf| { -/// conf.function(cdc_interface, |func| { -/// func.winusb_device(); -/// func.feature(VendorRevisionDescriptor::new(1)); -/// }); -/// conf.function(dfu_interface, |func| { -/// func.winusb_device(); -/// func.feature(VendorRevisionDescriptor::new(1)); -/// }); -/// }); -/// ``` -pub struct DeviceDescriptorSetBuilder<'a> { - builder: DescriptorSetBuilder<'a>, - windows_version: u32, - vendor_code: u8, -} +mod sealed { + use core::mem::size_of; -impl<'a> DeviceDescriptorSetBuilder<'a> { - /// Create a device descriptor set builder. - /// - /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] - /// module. - /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. - pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self { - let mut builder = DescriptorSetBuilder { used: 0, buf }; - builder.descriptor(DescriptorSetHeader { - wLength: (size_of::() as u16).to_le(), - wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(), - dwWindowsVersion: windows_version.to_le(), - wTotalLength: 0, - }); - Self { - builder, - windows_version, - vendor_code, + /// A trait for descriptors + pub trait Descriptor: Sized { + const TYPE: super::DescriptorType; + + /// The size of the descriptor's header. + fn size(&self) -> usize { + size_of::() } + + fn write_to(&self, buf: &mut [u8]); } - /// Add a device-level feature descriptor. - /// - /// Note that some feature descriptors may only be used at the device level in non-composite devices. - pub fn feature(&mut self, desc: T) - where - T: Descriptor + DeviceLevelDescriptor + 'a, - { - self.builder.descriptor(desc) - } - - /// Add a configuration subset. - pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) { - let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration); - build_conf(&mut cb); - self.builder.used += cb.finalize(); - } - - /// Finishes writing the data. - pub fn finalize(self) -> MsOsDescriptorSet<'a> { - let used = self.builder.used; - let buf = self.builder.buf; - // Update length in header with final length - let total_len = &mut buf[8..10]; - total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); - - MsOsDescriptorSet { - descriptor: &buf[..used], - windows_version: self.windows_version, - vendor_code: self.vendor_code, - } + pub trait DescriptorSet: Descriptor { + const LENGTH_OFFSET: usize; } } -pub struct ConfigurationSubsetBuilder<'a> { - builder: DescriptorSetBuilder<'a>, -} - -impl<'a> ConfigurationSubsetBuilder<'a> { - pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self { - let mut builder = DescriptorSetBuilder { used: 0, buf }; - builder.descriptor(ConfigurationSubsetHeader { - wLength: (size_of::() as u16).to_le(), - wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(), - bConfigurationValue: configuration, - bReserved: 0, - wTotalLength: 0, - }); - Self { builder } - } - - /// Add a function subset. - pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) { - let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface); - build_func(&mut fb); - self.builder.used += fb.finalize(); - } - - /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. - pub fn finalize(self) -> usize { - let used = self.builder.used; - let buf = self.builder.buf; - // Update length in header with final length - let total_len = &mut buf[6..8]; - total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); - used - } -} - -pub struct FunctionSubsetBuilder<'a> { - builder: DescriptorSetBuilder<'a>, -} - -impl<'a> FunctionSubsetBuilder<'a> { - pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self { - let mut builder = DescriptorSetBuilder { used: 0, buf }; - builder.descriptor(FunctionSubsetHeader { - wLength: (size_of::() as u16).to_le(), - wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(), - bFirstInterface: interface.0, - bReserved: 0, - wSubsetLength: 0, - }); - Self { builder } - } - - /// Add a function-level descriptor. - /// - /// Note that many descriptors can only be used at function-level in a composite device. - pub fn feature(&mut self, desc: T) - where - T: Descriptor + FunctionLevelDescriptor + 'a, - { - self.builder.descriptor(desc) - } - - /// Adds the feature descriptors to configure this function to use the WinUSB driver. - /// - /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the - /// USB_DEVICE GUID. - pub fn winusb_device(&mut self) { - self.feature(CompatibleIdFeatureDescriptor::new_winusb()); - self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid()); - } - - /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. - pub fn finalize(self) -> usize { - let used = self.builder.used; - let buf = self.builder.buf; - // Update length in header with final length - let total_len = &mut buf[6..8]; - total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); - used - } -} - -/// A trait for descriptors -pub trait Descriptor: Sized { - const TYPE: DescriptorType; - - /// The size of the descriptor's header. - fn size(&self) -> usize { - size_of::() - } - - fn write_to(&self, buf: &mut [u8]); -} +use sealed::*; /// Copies the data of `t` into `buf`. /// @@ -303,7 +266,7 @@ pub trait Descriptor: Sized { /// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); - assert!(buf.len() >= bytes.len()); + assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full"); (&mut buf[..bytes.len()]).copy_from_slice(bytes); } @@ -354,6 +317,17 @@ pub struct DescriptorSetHeader { wTotalLength: u16, } +impl DescriptorSetHeader { + pub fn new(windows_version: u32) -> Self { + DescriptorSetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + dwWindowsVersion: windows_version.to_le(), + wTotalLength: 0, + } + } +} + impl Descriptor for DescriptorSetHeader { const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; fn write_to(&self, buf: &mut [u8]) { @@ -361,6 +335,10 @@ impl Descriptor for DescriptorSetHeader { } } +impl DescriptorSet for DescriptorSetHeader { + const LENGTH_OFFSET: usize = 8; +} + /// Table 11. Configuration subset header. #[allow(non_snake_case)] #[repr(C, packed(1))] @@ -372,6 +350,18 @@ pub struct ConfigurationSubsetHeader { wTotalLength: u16, } +impl ConfigurationSubsetHeader { + pub fn new(config: u8) -> Self { + ConfigurationSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bConfigurationValue: config, + bReserved: 0, + wTotalLength: 0, + } + } +} + impl Descriptor for ConfigurationSubsetHeader { const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; fn write_to(&self, buf: &mut [u8]) { @@ -379,6 +369,10 @@ impl Descriptor for ConfigurationSubsetHeader { } } +impl DescriptorSet for ConfigurationSubsetHeader { + const LENGTH_OFFSET: usize = 6; +} + /// Table 12. Function subset header. #[allow(non_snake_case)] #[repr(C, packed(1))] @@ -390,6 +384,18 @@ pub struct FunctionSubsetHeader { wSubsetLength: u16, } +impl FunctionSubsetHeader { + pub fn new(first_interface: u8) -> Self { + FunctionSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bFirstInterface: first_interface, + bReserved: 0, + wSubsetLength: 0, + } + } +} + impl Descriptor for FunctionSubsetHeader { const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; fn write_to(&self, buf: &mut [u8]) { @@ -397,16 +403,17 @@ impl Descriptor for FunctionSubsetHeader { } } +impl DescriptorSet for FunctionSubsetHeader { + const LENGTH_OFFSET: usize = 6; +} + // Feature Descriptors /// A marker trait for feature descriptors that are valid at the device level. -pub trait DeviceLevelDescriptor {} +pub trait DeviceLevelDescriptor: Descriptor {} /// A marker trait for feature descriptors that are valid at the function level. -pub trait FunctionLevelDescriptor { - /// `true` when the feature descriptor may only be used at the function level in composite devices. - const COMPOSITE_ONLY: bool = false; -} +pub trait FunctionLevelDescriptor: Descriptor {} /// Table 13. Microsoft OS 2.0 compatible ID descriptor. #[allow(non_snake_case)] @@ -419,9 +426,7 @@ pub struct CompatibleIdFeatureDescriptor { } impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} -impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { - const COMPOSITE_ONLY: bool = true; -} +impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {} impl Descriptor for CompatibleIdFeatureDescriptor { const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; @@ -462,16 +467,12 @@ pub struct RegistryPropertyFeatureDescriptor<'a> { wLength: u16, wDescriptorType: u16, wPropertyDataType: u16, - wPropertyNameLength: u16, PropertyName: &'a [u8], - wPropertyDataLength: u16, PropertyData: &'a [u8], } impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} -impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { - const COMPOSITE_ONLY: bool = true; -} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; @@ -479,45 +480,22 @@ impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { 10 + self.PropertyName.len() + self.PropertyData.len() } fn write_to(&self, buf: &mut [u8]) { - assert!(buf.len() >= self.size()); - assert!(self.wPropertyNameLength as usize == self.PropertyName.len()); - assert!(self.wPropertyDataLength as usize == self.PropertyData.len()); + assert!(buf.len() >= self.size(), "MSOS descriptor buffer full"); write_u16(buf, 0..2, self.wLength); write_u16(buf, 2..4, self.wDescriptorType); write_u16(buf, 4..6, self.wPropertyDataType); - write_u16(buf, 6..8, self.wPropertyNameLength); + write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le()); let pne = 8 + self.PropertyName.len(); (&mut buf[8..pne]).copy_from_slice(self.PropertyName); let pds = pne + 2; let pde = pds + self.PropertyData.len(); - write_u16(buf, pne..pds, self.wPropertyDataLength); + write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le()); (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); } } impl<'a> RegistryPropertyFeatureDescriptor<'a> { - /// A registry property. - /// - /// `name` should be a NUL-terminated 16-bit Unicode string. - pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { - Self { - wLength: ((10 + name.len() + data.len()) as u16).to_le(), - wDescriptorType: (Self::TYPE as u16).to_le(), - wPropertyDataType: (data_type as u16).to_le(), - wPropertyNameLength: (name.len() as u16).to_le(), - PropertyName: name, - wPropertyDataLength: (data.len() as u16).to_le(), - PropertyData: data, - } - } - - fn u16str_bytes(s: &U16CStr) -> &[u8] { - unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } - } - - /// A registry property that sets the DeviceInterfaceGUIDs to the device interface class for USB devices which are - /// attached to a USB hub. - pub fn new_usb_deviceinterfaceguid() -> Self { + pub const DEVICE_INTERFACE_GUIDS_NAME: &U16CStr = { // Can't use defmt::panic in constant expressions (inside u16cstr!) macro_rules! panic { ($($x:tt)*) => { @@ -527,10 +505,25 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { }; } - Self::new_multi_string( - u16cstr!("DeviceInterfaceGUIDs"), - u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}").as_slice_with_nul(), - ) + u16cstr!("DeviceInterfaceGUIDs") + }; + + /// A registry property. + /// + /// `name` should be a NUL-terminated 16-bit Unicode string. + pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { + assert!(name.len() < usize::from(u16::MAX) && data.len() < usize::from(u16::MAX)); + Self { + wLength: ((10 + name.len() + data.len()) as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + wPropertyDataType: (data_type as u16).to_le(), + PropertyName: name, + PropertyData: data, + } + } + + fn u16str_bytes(s: &U16CStr) -> &[u8] { + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } } /// A registry property containing a NUL-terminated 16-bit Unicode string. @@ -558,6 +551,10 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { + assert!( + data.len() >= 2 && data[data.len() - 1] == 0 && data[data.len() - 2] == 0, + "multi-strings must end in double nul terminators" + ); Self::new_raw( Self::u16str_bytes(name), unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 95d93987..cfdda076 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] +msos-descriptor = ["embassy-usb/msos-descriptor"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] @@ -34,4 +35,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" -serde = { version = "1.0.136", default-features = false } \ No newline at end of file +serde = { version = "1.0.136", default-features = false } + +[[bin]] +name = "usb_serial_winusb" +required-features = ["msos-descriptor"] diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs new file mode 100644 index 00000000..443379a0 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +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::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::msos::{self, windows_version}; +use embassy_usb::{Builder, Config}; +use {defmt_rtt as _, panic_probe as _}; + +const DEVICE_INTERFACE_GUIDS: &[u16] = { + // Can't use defmt::panic in constant expressions (inside u16str!) + macro_rules! panic { + ($($x:tt)*) => { + { + ::core::panic!($($x)*); + } + }; + } + msos::u16str!("{EAA9A5DC-30BA-44BC-9232-606CDC875321}\0\0").as_slice() +}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let clock: pac::CLOCK = unsafe { mem::transmute(()) }; + + info!("Enabling ext hfosc..."); + clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); + while clock.events_hfclkstarted.read().bits() != 1 {} + + // Create the driver, from the HAL. + let irq = interrupt::take!(USBD); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut msos_descriptor, + &mut control_buf, + None, + ); + + builder.msos_descriptor(windows_version::WIN8_1, 2); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the + // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors. + // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. + let msos_writer = builder.msos_writer(); + msos_writer.configuration(0); + msos_writer.function(0); + msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new_multi_string( + msos::RegistryPropertyFeatureDescriptor::DEVICE_INTERFACE_GUIDS_NAME, + DEVICE_INTERFACE_GUIDS, + )); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( + class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From aa21aebb0b321a2085571e5be5fffcea4703584d Mon Sep 17 00:00:00 2001 From: alexmoon Date: Tue, 7 Feb 2023 14:19:51 -0500 Subject: [PATCH 340/374] Lazily encode UTF16 values and add docs --- embassy-usb/Cargo.toml | 3 +- embassy-usb/src/msos.rs | 318 +++++++++--------- .../nrf52840/src/bin/usb_serial_winusb.rs | 25 +- 3 files changed, 166 insertions(+), 180 deletions(-) diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 54a8f27c..eb9ba36f 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] -msos-descriptor = ["dep:widestring"] +msos-descriptor = [] default = ["usbd-hid"] [dependencies] @@ -25,7 +25,6 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" -widestring = { version = "1.0.2", default-features = false, optional = true } # for HID usbd-hid = { version = "0.6.0", optional = true } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 360f80d9..19ed3497 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -5,16 +5,9 @@ //! use core::mem::size_of; -use core::ops::Range; - -pub use widestring::{u16cstr, u16str, U16CStr, U16Str}; use super::{capability_type, BosWriter}; -fn write_u16>(buf: &mut [u8], range: Range, data: T) { - (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) -} - /// A serialized Microsoft OS 2.0 Descriptor set. /// /// Create with [`DeviceDescriptorSetBuilder`]. @@ -24,14 +17,17 @@ pub struct MsOsDescriptorSet<'d> { } impl<'d> MsOsDescriptorSet<'d> { + /// Gets the raw bytes of the MS OS descriptor pub fn descriptor(&self) -> &[u8] { self.descriptor } + /// Gets the vendor code used by the host to retrieve the MS OS descriptor pub fn vendor_code(&self) -> u8 { self.vendor_code } + /// Returns `true` if no MS OS descriptor data is available pub fn is_empty(&self) -> bool { self.descriptor.is_empty() } @@ -39,7 +35,7 @@ impl<'d> MsOsDescriptorSet<'d> { /// Writes a Microsoft OS 2.0 Descriptor set into a buffer. pub struct MsOsDescriptorWriter<'d> { - pub buf: &'d mut [u8], + buf: &'d mut [u8], position: usize, config_mark: Option, @@ -75,14 +71,17 @@ impl<'d> MsOsDescriptorWriter<'d> { } } + /// Returns `true` if the MS OS descriptor header has not yet been written pub fn is_empty(&self) -> bool { self.position == 0 } + /// Returns `true` if a configuration subset header has been started pub fn is_in_config_subset(&self) -> bool { self.config_mark.is_some() } + /// Returns `true` if a function subset header has been started and not yet ended pub fn is_in_function_subset(&self) -> bool { self.function_mark.is_some() } @@ -148,6 +147,7 @@ impl<'d> MsOsDescriptorWriter<'d> { self.write(desc); } + /// Ends the current function subset (if any) pub fn end_function(&mut self) { Self::end_subset::(self.buf, self.position, &mut self.function_mark); } @@ -212,29 +212,13 @@ impl<'d> MsOsDescriptorWriter<'d> { } } +/// Microsoft Windows version codes +/// +/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors. pub mod windows_version { - pub const WIN2K: u32 = 0x05000000; - pub const WIN2KSP1: u32 = 0x05000100; - pub const WIN2KSP2: u32 = 0x05000200; - pub const WIN2KSP3: u32 = 0x05000300; - pub const WIN2KSP4: u32 = 0x05000400; - - pub const WINXP: u32 = 0x05010000; - pub const WINXPSP1: u32 = 0x05010100; - pub const WINXPSP2: u32 = 0x05010200; - pub const WINXPSP3: u32 = 0x05010300; - pub const WINXPSP4: u32 = 0x05010400; - - pub const VISTA: u32 = 0x06000000; - pub const VISTASP1: u32 = 0x06000100; - pub const VISTASP2: u32 = 0x06000200; - pub const VISTASP3: u32 = 0x06000300; - pub const VISTASP4: u32 = 0x06000400; - - pub const WIN7: u32 = 0x06010000; - pub const WIN8: u32 = 0x06020000; - /// AKA `NTDDI_WINBLUE` + /// Windows 8.1 (aka `NTDDI_WINBLUE`) pub const WIN8_1: u32 = 0x06030000; + /// Windows 10 pub const WIN10: u32 = 0x0A000000; } @@ -266,7 +250,7 @@ use sealed::*; /// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); - assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full"); + assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full"); (&mut buf[..bytes.len()]).copy_from_slice(bytes); } @@ -274,14 +258,23 @@ unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u16)] pub enum DescriptorType { + /// MS OS descriptor set header SetHeaderDescriptor = 0, + /// Configuration subset header SubsetHeaderConfiguration = 1, + /// Function subset header SubsetHeaderFunction = 2, + /// Compatible device ID feature descriptor FeatureCompatibleId = 3, + /// Registry property feature descriptor FeatureRegProperty = 4, + /// Minimum USB resume time feature descriptor FeatureMinResumeTime = 5, + /// Vendor revision feature descriptor FeatureModelId = 6, + /// CCGP device descriptor feature descriptor FeatureCcgpDevice = 7, + /// Vendor revision feature descriptor FeatureVendorRevision = 8, } @@ -318,6 +311,9 @@ pub struct DescriptorSetHeader { } impl DescriptorSetHeader { + /// Creates a MS OS descriptor set header. + /// + /// `windows_version` is the minimum Windows version the descriptor set can apply to. pub fn new(windows_version: u32) -> Self { DescriptorSetHeader { wLength: (size_of::() as u16).to_le(), @@ -351,6 +347,7 @@ pub struct ConfigurationSubsetHeader { } impl ConfigurationSubsetHeader { + /// Creates a configuration subset header pub fn new(config: u8) -> Self { ConfigurationSubsetHeader { wLength: (size_of::() as u16).to_le(), @@ -385,6 +382,7 @@ pub struct FunctionSubsetHeader { } impl FunctionSubsetHeader { + /// Creates a function subset header pub fn new(first_interface: u8) -> Self { FunctionSubsetHeader { wLength: (size_of::() as u16).to_le(), @@ -436,11 +434,8 @@ impl Descriptor for CompatibleIdFeatureDescriptor { } impl CompatibleIdFeatureDescriptor { - /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty. - pub fn new_winusb() -> Self { - Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8]) - } - + /// Creates a compatible ID feature descriptor + /// /// The ids must be 8 ASCII bytes or fewer. pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); @@ -451,7 +446,7 @@ impl CompatibleIdFeatureDescriptor { Self::new_raw(cid, scid) } - pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { + fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { Self { wLength: (size_of::() as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(), @@ -464,129 +459,87 @@ impl CompatibleIdFeatureDescriptor { /// Table 14. Microsoft OS 2.0 registry property descriptor #[allow(non_snake_case)] pub struct RegistryPropertyFeatureDescriptor<'a> { - wLength: u16, - wDescriptorType: u16, - wPropertyDataType: u16, - PropertyName: &'a [u8], - PropertyData: &'a [u8], + name: &'a str, + data: PropertyData<'a>, } -impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} -impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} - -impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { - const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; - fn size(&self) -> usize { - 10 + self.PropertyName.len() + self.PropertyData.len() - } - fn write_to(&self, buf: &mut [u8]) { - assert!(buf.len() >= self.size(), "MSOS descriptor buffer full"); - write_u16(buf, 0..2, self.wLength); - write_u16(buf, 2..4, self.wDescriptorType); - write_u16(buf, 4..6, self.wPropertyDataType); - write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le()); - let pne = 8 + self.PropertyName.len(); - (&mut buf[8..pne]).copy_from_slice(self.PropertyName); - let pds = pne + 2; - let pde = pds + self.PropertyData.len(); - write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le()); - (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); - } -} - -impl<'a> RegistryPropertyFeatureDescriptor<'a> { - pub const DEVICE_INTERFACE_GUIDS_NAME: &U16CStr = { - // Can't use defmt::panic in constant expressions (inside u16cstr!) - macro_rules! panic { - ($($x:tt)*) => { - { - ::core::panic!($($x)*); - } - }; - } - - u16cstr!("DeviceInterfaceGUIDs") - }; - - /// A registry property. - /// - /// `name` should be a NUL-terminated 16-bit Unicode string. - pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { - assert!(name.len() < usize::from(u16::MAX) && data.len() < usize::from(u16::MAX)); - Self { - wLength: ((10 + name.len() + data.len()) as u16).to_le(), - wDescriptorType: (Self::TYPE as u16).to_le(), - wPropertyDataType: (data_type as u16).to_le(), - PropertyName: name, - PropertyData: data, - } - } - - fn u16str_bytes(s: &U16CStr) -> &[u8] { - unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } - } - - /// A registry property containing a NUL-terminated 16-bit Unicode string. - pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { - Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) - } - - /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables. - pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - Self::u16str_bytes(data), - PropertyDataType::ExpandSz, - ) - } - - /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link. - pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - Self::u16str_bytes(data), - PropertyDataType::Link, - ) - } - - /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. - pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { - assert!( - data.len() >= 2 && data[data.len() - 1] == 0 && data[data.len() - 2] == 0, - "multi-strings must end in double nul terminators" - ); - Self::new_raw( - Self::u16str_bytes(name), - unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, - PropertyDataType::RegMultiSz, - ) - } - +/// Data values that can be encoded into a registry property descriptor +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PropertyData<'a> { + /// A registry property containing a string. + Sz(&'a str), + /// A registry property containing a string that expands environment variables. + ExpandSz(&'a str), /// A registry property containing binary data. - pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self { - Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary) - } - - /// A registry property containing a Little-Endian 32-bit integer. - /// - /// The function assumes that `data` is already little-endian, it does not convert it. - pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, - PropertyDataType::DwordLittleEndian, - ) - } - + Binary(&'a [u8]), + /// A registry property containing a little-endian 32-bit integer. + DwordLittleEndian(u32), /// A registry property containing a big-endian 32-bit integer. - /// - /// The function assumes that `data` is already big-endian, it does not convert it. - pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, - PropertyDataType::DwordBigEndian, - ) + DwordBigEndian(u32), + /// A registry property containing a string that contains a symbolic link. + Link(&'a str), + /// A registry property containing multiple strings. + RegMultiSz(&'a [&'a str]), +} + +fn write_bytes(val: &[u8], buf: &mut [u8]) -> usize { + assert!(buf.len() >= val.len()); + buf[..val.len()].copy_from_slice(val); + val.len() +} + +fn write_utf16(val: &str, buf: &mut [u8]) -> usize { + let mut pos = 0; + for c in val.encode_utf16() { + pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]); + } + pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..]) +} + +impl<'a> PropertyData<'a> { + /// Gets the `PropertyDataType` for this property value + pub fn kind(&self) -> PropertyDataType { + match self { + PropertyData::Sz(_) => PropertyDataType::Sz, + PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz, + PropertyData::Binary(_) => PropertyDataType::Binary, + PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian, + PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian, + PropertyData::Link(_) => PropertyDataType::Link, + PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz, + } + } + + /// Gets the size (in bytes) of this property value when encoded. + pub fn size(&self) -> usize { + match self { + PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => { + core::mem::size_of::() * (val.encode_utf16().count() + 1) + } + PropertyData::Binary(val) => val.len(), + PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val), + PropertyData::RegMultiSz(val) => { + core::mem::size_of::() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1 + } + } + } + + /// Encodes the data for this property value and writes it to `buf`. + pub fn write(&self, buf: &mut [u8]) -> usize { + match self { + PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf), + PropertyData::Binary(val) => write_bytes(val, buf), + PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf), + PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf), + PropertyData::RegMultiSz(val) => { + let mut pos = 0; + for s in *val { + pos += write_utf16(s, &mut buf[pos..]); + } + pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..]) + } + } } } @@ -594,15 +547,57 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u16)] pub enum PropertyDataType { + /// A registry property containing a string. Sz = 1, + /// A registry property containing a string that expands environment variables. ExpandSz = 2, + /// A registry property containing binary data. Binary = 3, + /// A registry property containing a little-endian 32-bit integer. DwordLittleEndian = 4, + /// A registry property containing a big-endian 32-bit integer. DwordBigEndian = 5, + /// A registry property containing a string that contains a symbolic link. Link = 6, + /// A registry property containing multiple strings. RegMultiSz = 7, } +impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} + +impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { + const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; + + fn size(&self) -> usize { + 10 + self.name_size() + self.data.size() + } + + fn write_to(&self, buf: &mut [u8]) { + assert!(buf.len() >= self.size(), "MS OS descriptor buffer full"); + + let mut pos = 0; + pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_utf16(self.name, &mut buf[pos..]); + pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]); + self.data.write(&mut buf[pos..]); + } +} + +impl<'a> RegistryPropertyFeatureDescriptor<'a> { + /// A registry property. + pub fn new(name: &'a str, data: PropertyData<'a>) -> Self { + Self { name, data } + } + + fn name_size(&self) -> usize { + core::mem::size_of::() * (self.name.encode_utf16().count() + 1) + } +} + /// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor. #[allow(non_snake_case)] #[repr(C, packed(1))] @@ -658,15 +653,14 @@ impl Descriptor for ModelIdDescriptor { } impl ModelIdDescriptor { + /// Creates a new model ID descriptor + /// + /// `model_id` should be a uuid that uniquely identifies a physical device. pub fn new(model_id: u128) -> Self { - Self::new_bytes(model_id.to_le_bytes()) - } - - pub fn new_bytes(model_id: [u8; 16]) -> Self { Self { wLength: (size_of::() as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(), - modelId: model_id, + modelId: model_id.to_le_bytes(), } } } @@ -689,6 +683,7 @@ impl Descriptor for CcgpDeviceDescriptor { } impl CcgpDeviceDescriptor { + /// Creates a new CCGP device descriptor pub fn new() -> Self { Self { wLength: (size_of::() as u16).to_le(), @@ -704,7 +699,7 @@ pub struct VendorRevisionDescriptor { wLength: u16, wDescriptorType: u16, /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or - /// other MSOS descriptor. Shell set to greater than or equal to 1. + /// other MS OS descriptor. Shell set to greater than or equal to 1. VendorRevision: u16, } @@ -719,6 +714,7 @@ impl Descriptor for VendorRevisionDescriptor { } impl VendorRevisionDescriptor { + /// Creates a new vendor revision descriptor pub fn new(revision: u16) -> Self { assert!(revision >= 1); Self { diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 443379a0..f4b828de 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -7,7 +7,7 @@ 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::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -15,17 +15,8 @@ use embassy_usb::msos::{self, windows_version}; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; -const DEVICE_INTERFACE_GUIDS: &[u16] = { - // Can't use defmt::panic in constant expressions (inside u16str!) - macro_rules! panic { - ($($x:tt)*) => { - { - ::core::panic!($($x)*); - } - }; - } - msos::u16str!("{EAA9A5DC-30BA-44BC-9232-606CDC875321}\0\0").as_slice() -}; +// This is a randomly generated GUID to allow clients on Windows to find our device +const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -39,7 +30,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -89,9 +80,9 @@ async fn main(_spawner: Spawner) { msos_writer.configuration(0); msos_writer.function(0); msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); - msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new_multi_string( - msos::RegistryPropertyFeatureDescriptor::DEVICE_INTERFACE_GUIDS_NAME, - DEVICE_INTERFACE_GUIDS, + msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), )); // Build the builder. @@ -126,7 +117,7 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( +async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; From 1d841cc8ac74feacc4d231958ce2c46419ae3bda Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 20:49:10 +0100 Subject: [PATCH 341/374] usb: make max interface count configurable at compile time. --- embassy-usb/Cargo.toml | 13 ++++++ embassy-usb/README.md | 24 +++++++++- embassy-usb/build.rs | 93 ++++++++++++++++++++++++++++++++++++++ embassy-usb/gen_config.py | 73 ++++++++++++++++++++++++++++++ embassy-usb/src/builder.rs | 5 +- embassy-usb/src/lib.rs | 9 ++-- 6 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 embassy-usb/build.rs create mode 100644 embassy-usb/gen_config.py diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index eb9ba36f..463e1268 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -16,6 +16,19 @@ usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] msos-descriptor = [] default = ["usbd-hid"] +# BEGIN AUTOGENERATED CONFIG FEATURES +# Generated by gen_config.py. DO NOT EDIT. +max-interface-count-1 = [] +max-interface-count-2 = [] +max-interface-count-3 = [] +max-interface-count-4 = [] # Default +max-interface-count-5 = [] +max-interface-count-6 = [] +max-interface-count-7 = [] +max-interface-count-8 = [] + +# END AUTOGENERATED CONFIG FEATURES + [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } diff --git a/embassy-usb/README.md b/embassy-usb/README.md index 581c3290..a3d45b56 100644 --- a/embassy-usb/README.md +++ b/embassy-usb/README.md @@ -1,6 +1,28 @@ # embassy-usb -TODO crate description/ +TODO crate description + +## Configuration + +`embassy-usb` has some configuration settings that are set at compile time, affecting sizes +and counts of buffers. + +They can be set in two ways: + +- Via Cargo features: enable a feature like `-`. `name` must be in lowercase and +use dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values +is available, check `Cargo.toml` for the list. +- Via environment variables at build time: set the variable named `EMBASSY_USB_`. For example +`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. +Any value can be set, unlike with Cargo features. + +Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting +with different values, compilation fails. + +### `MAX_INTERFACE_COUNT` + +Max amount of interfaces that can be created in one device. Default: 4. + ## Interoperability diff --git a/embassy-usb/build.rs b/embassy-usb/build.rs new file mode 100644 index 00000000..524bdc2f --- /dev/null +++ b/embassy-usb/build.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; +use std::fmt::Write; +use std::path::PathBuf; +use std::{env, fs}; + +static CONFIGS: &[(&str, usize)] = &[ + // BEGIN AUTOGENERATED CONFIG FEATURES + // Generated by gen_config.py. DO NOT EDIT. + ("MAX_INTERFACE_COUNT", 4), + // END AUTOGENERATED CONFIG FEATURES +]; + +struct ConfigState { + value: usize, + seen_feature: bool, + seen_env: bool, +} + +fn main() { + let crate_name = env::var("CARGO_PKG_NAME") + .unwrap() + .to_ascii_uppercase() + .replace('-', "_"); + + // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any + // other file changed. + println!("cargo:rerun-if-changed=build.rs"); + + // Rebuild if config envvar changed. + for (name, _) in CONFIGS { + println!("cargo:rerun-if-env-changed={crate_name}_{name}"); + } + + let mut configs = HashMap::new(); + for (name, default) in CONFIGS { + configs.insert( + *name, + ConfigState { + value: *default, + seen_env: false, + seen_feature: false, + }, + ); + } + + let prefix = format!("{crate_name}_"); + for (var, value) in env::vars() { + if let Some(name) = var.strip_prefix(&prefix) { + let Some(cfg) = configs.get_mut(name) else { + panic!("Unknown env var {name}") + }; + + let Ok(value) = value.parse::() else { + panic!("Invalid value for env var {name}: {value}") + }; + + cfg.value = value; + cfg.seen_env = true; + } + + if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") { + if let Some(i) = feature.rfind('_') { + let name = &feature[..i]; + let value = &feature[i + 1..]; + if let Some(cfg) = configs.get_mut(name) { + let Ok(value) = value.parse::() else { + panic!("Invalid value for feature {name}: {value}") + }; + + // envvars take priority. + if !cfg.seen_env { + if cfg.seen_feature { + panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value); + } + + cfg.value = value; + cfg.seen_feature = true; + } + } + } + } + } + + let mut data = String::new(); + + for (name, cfg) in &configs { + writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap(); + } + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); + fs::write(out_file, data).unwrap(); +} diff --git a/embassy-usb/gen_config.py b/embassy-usb/gen_config.py new file mode 100644 index 00000000..55a7fa3c --- /dev/null +++ b/embassy-usb/gen_config.py @@ -0,0 +1,73 @@ +import os + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +features = [] + + +def feature(name, default, min, max, pow2=None): + vals = set() + val = min + while val <= max: + vals.add(val) + if pow2 == True or (isinstance(pow2, int) and val >= pow2): + val *= 2 + else: + val += 1 + vals.add(default) + + features.append( + { + "name": name, + "default": default, + "vals": sorted(list(vals)), + } + ) + + +feature("max_interface_count", default=4, min=1, max=8) + +# ========= Update Cargo.toml + +things = "" +for f in features: + name = f["name"].replace("_", "-") + for val in f["vals"]: + things += f"{name}-{val} = []" + if val == f["default"]: + things += " # Default" + things += "\n" + things += "\n" + +SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n" +HELP = "# Generated by gen_config.py. DO NOT EDIT.\n" +with open("Cargo.toml", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after +with open("Cargo.toml", "w") as f: + f.write(data) + + +# ========= Update build.rs + +things = "" +for f in features: + name = f["name"].upper() + things += f' ("{name}", {f["default"]}),\n' + +SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n" +HELP = " // Generated by gen_config.py. DO NOT EDIT.\n" +with open("build.rs", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + \ + things + " " + SEPARATOR_END + after +with open("build.rs", "w") as f: + f.write(data) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index d1cbf674..d89fc401 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -308,7 +308,10 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { }; if self.builder.interfaces.push(iface).is_err() { - panic!("max interface count reached") + panic!( + "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}", + MAX_INTERFACE_COUNT + ) } InterfaceBuilder { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index aec18524..f8983318 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -16,10 +16,16 @@ mod descriptor_reader; pub mod msos; pub mod types; +mod config { + #![allow(unused)] + include!(concat!(env!("OUT_DIR"), "/config.rs")); +} + use embassy_futures::select::{select, Either}; use heapless::Vec; pub use crate::builder::{Builder, Config}; +use crate::config::*; use crate::control::*; use crate::descriptor::*; use crate::descriptor_reader::foreach_endpoint; @@ -71,9 +77,6 @@ pub const CONFIGURATION_NONE: u8 = 0; /// The bConfiguration value for the single configuration supported by this device. pub const CONFIGURATION_VALUE: u8 = 1; -/// Maximum interface count, configured at compile time. -pub const MAX_INTERFACE_COUNT: usize = 4; - const STRING_INDEX_MANUFACTURER: u8 = 1; const STRING_INDEX_PRODUCT: u8 = 2; const STRING_INDEX_SERIAL_NUMBER: u8 = 3; From 3af991ab63d14cfad6f50d28bfb944d1895d1c70 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 22:49:14 +0100 Subject: [PATCH 342/374] usb: unify ControlHandler+DeviceStateHandler, route all control requests to all handlers. - Allows classes to handle vendor requests. - Allows classes to use a single handler for multiple interfaces. - Allows classes to access the other events (previously only `reset` was available). --- embassy-usb-logger/src/lib.rs | 1 - embassy-usb/Cargo.toml | 9 + embassy-usb/build.rs | 1 + embassy-usb/gen_config.py | 1 + embassy-usb/src/builder.rs | 45 ++-- embassy-usb/src/class/cdc_acm.rs | 50 ++-- embassy-usb/src/class/cdc_ncm/mod.rs | 83 +++--- embassy-usb/src/class/hid.rs | 172 +++++++------ embassy-usb/src/control.rs | 58 ----- embassy-usb/src/descriptor_reader.rs | 7 +- embassy-usb/src/lib.rs | 243 +++++++++++------- embassy-usb/src/types.rs | 6 +- examples/nrf52840/src/bin/usb_ethernet.rs | 1 - examples/nrf52840/src/bin/usb_hid_keyboard.rs | 25 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 1 - examples/nrf52840/src/bin/usb_serial.rs | 1 - .../nrf52840/src/bin/usb_serial_multitask.rs | 1 - .../nrf52840/src/bin/usb_serial_winusb.rs | 1 - examples/rp/src/bin/usb_ethernet.rs | 1 - examples/rp/src/bin/usb_serial.rs | 1 - examples/stm32f1/src/bin/usb_serial.rs | 1 - examples/stm32f3/src/bin/usb_serial.rs | 1 - examples/stm32f4/src/bin/usb_ethernet.rs | 1 - examples/stm32f4/src/bin/usb_serial.rs | 1 - examples/stm32f7/src/bin/usb_serial.rs | 1 - examples/stm32h7/src/bin/usb_serial.rs | 1 - examples/stm32l4/src/bin/usb_serial.rs | 1 - examples/stm32l5/src/bin/usb_ethernet.rs | 1 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 1 - examples/stm32l5/src/bin/usb_serial.rs | 1 - examples/stm32u5/src/bin/usb_serial.rs | 1 - 31 files changed, 381 insertions(+), 338 deletions(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 6386e209..1d8dd13c 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -74,7 +74,6 @@ impl UsbLogger { &mut state.config_descriptor, &mut state.bos_descriptor, &mut state.control_buf, - None, ); // Create classes on the builder. diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 463e1268..ae3f3ac3 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -27,6 +27,15 @@ max-interface-count-6 = [] max-interface-count-7 = [] max-interface-count-8 = [] +max-handler-count-1 = [] +max-handler-count-2 = [] +max-handler-count-3 = [] +max-handler-count-4 = [] # Default +max-handler-count-5 = [] +max-handler-count-6 = [] +max-handler-count-7 = [] +max-handler-count-8 = [] + # END AUTOGENERATED CONFIG FEATURES [dependencies] diff --git a/embassy-usb/build.rs b/embassy-usb/build.rs index 524bdc2f..33d32f7d 100644 --- a/embassy-usb/build.rs +++ b/embassy-usb/build.rs @@ -7,6 +7,7 @@ static CONFIGS: &[(&str, usize)] = &[ // BEGIN AUTOGENERATED CONFIG FEATURES // Generated by gen_config.py. DO NOT EDIT. ("MAX_INTERFACE_COUNT", 4), + ("MAX_HANDLER_COUNT", 4), // END AUTOGENERATED CONFIG FEATURES ]; diff --git a/embassy-usb/gen_config.py b/embassy-usb/gen_config.py index 55a7fa3c..67ce359c 100644 --- a/embassy-usb/gen_config.py +++ b/embassy-usb/gen_config.py @@ -28,6 +28,7 @@ def feature(name, default, min, max, pow2=None): feature("max_interface_count", default=4, min=1, max=8) +feature("max_handler_count", default=4, min=1, max=8) # ========= Update Cargo.toml diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index d89fc401..07b2a177 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,12 +1,12 @@ use heapless::Vec; -use crate::control::ControlHandler; +use crate::config::*; use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::driver::{Driver, Endpoint, EndpointType}; #[cfg(feature = "msos-descriptor")] use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::*; -use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; +use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -122,8 +122,8 @@ impl<'a> Config<'a> { /// [`UsbDevice`] builder. pub struct Builder<'d, D: Driver<'d>> { config: Config<'d>, - handler: Option<&'d dyn DeviceStateHandler>, - interfaces: Vec, MAX_INTERFACE_COUNT>, + handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, + interfaces: Vec, control_buf: &'d mut [u8], driver: D, @@ -151,7 +151,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { bos_descriptor_buf: &'d mut [u8], #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], control_buf: &'d mut [u8], - handler: Option<&'d dyn DeviceStateHandler>, ) -> Self { // Magic values specified in USB-IF ECN on IADs. if config.composite_with_iads @@ -179,9 +178,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { Builder { driver, - handler, config, interfaces: Vec::new(), + handlers: Vec::new(), control_buf, next_string_index: STRING_INDEX_CUSTOM_START, @@ -205,7 +204,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { UsbDevice::build( self.driver, self.config, - self.handler, + self.handlers, self.device_descriptor.into_buf(), self.config_descriptor.into_buf(), self.bos_descriptor.writer.into_buf(), @@ -248,6 +247,26 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { } } + /// Add a Handler. + /// + /// The Handler is called on some USB bus events, and to handle all control requests not already + /// handled by the USB stack. + pub fn handler(&mut self, handler: &'d mut dyn Handler) { + if self.handlers.push(handler).is_err() { + panic!( + "embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}", + MAX_HANDLER_COUNT + ) + } + } + + /// Allocates a new string index. + pub fn string(&mut self) -> StringIndex { + let index = self.next_string_index; + self.next_string_index += 1; + StringIndex::new(index) + } + #[cfg(feature = "msos-descriptor")] /// Add an MS OS 2.0 Descriptor Set. /// @@ -301,10 +320,8 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { let number = self.builder.interfaces.len() as _; let iface = Interface { - handler: None, current_alt_setting: 0, num_alt_settings: 0, - num_strings: 0, }; if self.builder.interfaces.push(iface).is_err() { @@ -350,17 +367,9 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { self.interface_number } - pub fn handler(&mut self, handler: &'d mut dyn ControlHandler) { - self.builder.interfaces[self.interface_number.0 as usize].handler = Some(handler); - } - /// Allocates a new string index. pub fn string(&mut self) -> StringIndex { - let index = self.builder.next_string_index; - self.builder.next_string_index += 1; - self.builder.interfaces[self.interface_number.0 as usize].num_strings += 1; - - StringIndex::new(index) + self.builder.string() } /// Add an alternate setting to the interface and write its descriptor. diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index fb9eaeca..ff82ad40 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::blocking_mutex::CriticalSectionMutex; -use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; -use crate::Builder; +use crate::{Builder, Handler}; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; @@ -67,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> { } struct Control<'a> { + comm_if: InterfaceNumber, shared: &'a ControlShared, } @@ -98,7 +99,7 @@ impl<'a> Control<'a> { } } -impl<'d> ControlHandler for Control<'d> { +impl<'d> Handler for Control<'d> { fn reset(&mut self) { let shared = self.shared(); shared.line_coding.lock(|x| x.set(LineCoding::default())); @@ -106,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> { shared.rts.store(false, Ordering::Relaxed); } - fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse { + fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { REQ_SEND_ENCAPSULATED_COMMAND => { // We don't actually support encapsulated commands but pretend we do for standards // compatibility. - OutResponse::Accepted + Some(OutResponse::Accepted) } REQ_SET_LINE_CODING if data.len() >= 7 => { let coding = LineCoding { @@ -123,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> { self.shared().line_coding.lock(|x| x.set(coding)); debug!("Set line coding to: {:?}", coding); - OutResponse::Accepted + Some(OutResponse::Accepted) } REQ_SET_CONTROL_LINE_STATE => { let dtr = (req.value & 0x0001) != 0; @@ -134,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> { shared.rts.store(rts, Ordering::Relaxed); debug!("Set dtr {}, rts {}", dtr, rts); - OutResponse::Accepted + Some(OutResponse::Accepted) } - _ => OutResponse::Rejected, + _ => Some(OutResponse::Rejected), } } - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. REQ_GET_LINE_CODING if req.length == 7 => { @@ -151,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> { buf[4] = coding.stop_bits as u8; buf[5] = coding.parity_type as u8; buf[6] = coding.data_bits; - InResponse::Accepted(&buf[0..7]) + Some(InResponse::Accepted(&buf[0..7])) } - _ => InResponse::Rejected, + _ => Some(InResponse::Rejected), } } } @@ -162,17 +175,12 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self { - let control = state.control.write(Control { shared: &state.shared }); - - let control_shared = &state.shared; - assert!(builder.control_buf_len() >= 7); let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); // Control interface let mut iface = func.interface(); - iface.handler(control); let comm_if = iface.interface_number(); let data_if = u8::from(comm_if) + 1; let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); @@ -213,6 +221,16 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); + drop(func); + + let control = state.control.write(Control { + shared: &state.shared, + comm_if, + }); + builder.handler(control); + + let control_shared = &state.shared; + CdcAcmClass { _comm_ep: comm_ep, _data_if: data_if, diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index d6c7d37e..262499cc 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -17,10 +17,10 @@ use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; -use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; -use crate::Builder; +use crate::{Builder, Handler}; pub mod embassy_net; @@ -117,8 +117,7 @@ fn byteify(buf: &mut [u8], data: T) -> &[u8] { /// Internal state for the CDC-NCM class. pub struct State<'a> { - comm_control: MaybeUninit>, - data_control: MaybeUninit, + control: MaybeUninit>, shared: ControlShared, } @@ -126,8 +125,7 @@ impl<'a> State<'a> { /// Create a new `State`. pub fn new() -> Self { Self { - comm_control: MaybeUninit::uninit(), - data_control: MaybeUninit::uninit(), + control: MaybeUninit::uninit(), shared: Default::default(), } } @@ -144,29 +142,55 @@ impl Default for ControlShared { } } -struct CommControl<'a> { +struct Control<'a> { mac_addr_string: StringIndex, shared: &'a ControlShared, mac_addr_str: [u8; 12], + comm_if: InterfaceNumber, + data_if: InterfaceNumber, } -impl<'d> ControlHandler for CommControl<'d> { - fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse { +impl<'d> Handler for Control<'d> { + fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { + if iface != self.data_if { + return; + } + + match alternate_setting { + ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), + ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), + _ => unreachable!(), + } + } + + fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { REQ_SEND_ENCAPSULATED_COMMAND => { // We don't actually support encapsulated commands but pretend we do for standards // compatibility. - OutResponse::Accepted + Some(OutResponse::Accepted) } REQ_SET_NTB_INPUT_SIZE => { // TODO - OutResponse::Accepted + Some(OutResponse::Accepted) } - _ => OutResponse::Rejected, + _ => Some(OutResponse::Rejected), } } - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { REQ_GET_NTB_PARAMETERS => { let res = NtbParameters { @@ -187,9 +211,9 @@ impl<'d> ControlHandler for CommControl<'d> { max_datagram_count: 1, // We only decode 1 packet per NTB }, }; - InResponse::Accepted(byteify(buf, res)) + Some(InResponse::Accepted(byteify(buf, res))) } - _ => InResponse::Rejected, + _ => Some(InResponse::Rejected), } } @@ -214,18 +238,6 @@ impl<'d> ControlHandler for CommControl<'d> { } } -struct DataControl {} - -impl ControlHandler for DataControl { - fn set_alternate_setting(&mut self, alternate_setting: u8) { - match alternate_setting { - ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), - ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), - _ => unreachable!(), - } - } -} - /// CDC-NCM class pub struct CdcNcmClass<'d, D: Driver<'d>> { _comm_if: InterfaceNumber, @@ -253,11 +265,6 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { // Control interface let mut iface = func.interface(); let mac_addr_string = iface.string(); - iface.handler(state.comm_control.write(CommControl { - mac_addr_string, - shared: &state.shared, - mac_addr_str: [0; 12], - })); let comm_if = iface.interface_number(); let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); @@ -307,13 +314,23 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { // Data interface let mut iface = func.interface(); - iface.handler(state.data_control.write(DataControl {})); let data_if = iface.interface_number(); let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); + drop(func); + + let control = state.control.write(Control { + mac_addr_string, + shared: &state.shared, + mac_addr_str: [0; 12], + comm_if, + data_if, + }); + builder.handler(control); + CdcNcmClass { _comm_if: comm_if, comm_ep, diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 0283c112..974268c6 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -9,9 +9,10 @@ use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; -use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; +use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use crate::Builder; +use crate::types::InterfaceNumber; +use crate::{Builder, Handler}; const USB_CLASS_HID: u8 = 0x03; const USB_SUBCLASS_NONE: u8 = 0x00; @@ -100,17 +101,11 @@ fn build<'d, D: Driver<'d>>( config: Config<'d>, with_out_endpoint: bool, ) -> (Option, D::EndpointIn, &'d AtomicUsize) { - let control = state.control.write(Control::new( - config.report_descriptor, - config.request_handler, - &state.out_report_offset, - )); - let len = config.report_descriptor.len(); let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); let mut iface = func.interface(); - iface.handler(control); + let if_num = iface.interface_number(); let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); // HID descriptor @@ -139,6 +134,16 @@ fn build<'d, D: Driver<'d>>( None }; + drop(func); + + let control = state.control.write(Control::new( + if_num, + config.report_descriptor, + config.request_handler, + &state.out_report_offset, + )); + builder.handler(control); + (ep_out, ep_in, &state.out_report_offset) } @@ -400,6 +405,7 @@ pub trait RequestHandler { } struct Control<'d> { + if_num: InterfaceNumber, report_descriptor: &'d [u8], request_handler: Option<&'d dyn RequestHandler>, out_report_offset: &'d AtomicUsize, @@ -408,11 +414,13 @@ struct Control<'d> { impl<'d> Control<'d> { fn new( + if_num: InterfaceNumber, report_descriptor: &'d [u8], request_handler: Option<&'d dyn RequestHandler>, out_report_offset: &'d AtomicUsize, ) -> Self { Control { + if_num, report_descriptor, request_handler, out_report_offset, @@ -438,88 +446,100 @@ impl<'d> Control<'d> { } } -impl<'d> ControlHandler for Control<'d> { +impl<'d> Handler for Control<'d> { fn reset(&mut self) { self.out_report_offset.store(0, Ordering::Release); } - fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> { - match (req.value >> 8) as u8 { - HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), - HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), - _ => InResponse::Rejected, + fn control_out(&mut self, req: Request, data: &[u8]) -> Option { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.if_num.0 as u16) + { + return None; } - } - 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 { - HID_REQ_SET_IDLE => { - if let Some(handler) = self.request_handler { - let id = req.value as u8; - let id = (id != 0).then(|| ReportId::In(id)); - let dur = u32::from(req.value >> 8); - let dur = if dur == 0 { u32::MAX } else { 4 * dur }; - handler.set_idle_ms(id, dur); - } - OutResponse::Accepted - } - HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { - (Ok(id), Some(handler)) => handler.set_report(id, data), - _ => OutResponse::Rejected, - }, - HID_REQ_SET_PROTOCOL => { - if req.value == 1 { - OutResponse::Accepted - } else { - warn!("HID Boot Protocol is unsupported."); - OutResponse::Rejected // UNSUPPORTED: Boot Protocol - } - } - _ => OutResponse::Rejected, - } - } else { - OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR - } - } - - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { - trace!("HID control_in {:?}", req); match req.request { - HID_REQ_GET_REPORT => { - let size = match ReportId::try_from(req.value) { - Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), - Err(_) => None, - }; - - if let Some(size) = size { - InResponse::Accepted(&buf[0..size]) - } else { - InResponse::Rejected - } - } - HID_REQ_GET_IDLE => { + HID_REQ_SET_IDLE => { if let Some(handler) = self.request_handler { let id = req.value as u8; let id = (id != 0).then(|| ReportId::In(id)); - if let Some(dur) = handler.get_idle_ms(id) { - let dur = u8::try_from(dur / 4).unwrap_or(0); - buf[0] = dur; - InResponse::Accepted(&buf[0..1]) - } else { - InResponse::Rejected - } + let dur = u32::from(req.value >> 8); + let dur = if dur == 0 { u32::MAX } else { 4 * dur }; + handler.set_idle_ms(id, dur); + } + Some(OutResponse::Accepted) + } + HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { + (Ok(id), Some(handler)) => Some(handler.set_report(id, data)), + _ => Some(OutResponse::Rejected), + }, + HID_REQ_SET_PROTOCOL => { + if req.value == 1 { + Some(OutResponse::Accepted) } else { - InResponse::Rejected + warn!("HID Boot Protocol is unsupported."); + Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol } } - HID_REQ_GET_PROTOCOL => { - // UNSUPPORTED: Boot Protocol - buf[0] = 1; - InResponse::Accepted(&buf[0..1]) + _ => Some(OutResponse::Rejected), + } + } + + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + if req.index != self.if_num.0 as u16 { + return None; + } + + match (req.request_type, req.recipient) { + (RequestType::Standard, Recipient::Interface) => match req.request { + Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 { + HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)), + HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)), + _ => Some(InResponse::Rejected), + }, + + _ => Some(InResponse::Rejected), + }, + (RequestType::Class, Recipient::Interface) => { + trace!("HID control_in {:?}", req); + match req.request { + HID_REQ_GET_REPORT => { + let size = match ReportId::try_from(req.value) { + Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), + Err(_) => None, + }; + + if let Some(size) = size { + Some(InResponse::Accepted(&buf[0..size])) + } else { + Some(InResponse::Rejected) + } + } + HID_REQ_GET_IDLE => { + if let Some(handler) = self.request_handler { + let id = req.value as u8; + let id = (id != 0).then(|| ReportId::In(id)); + if let Some(dur) = handler.get_idle_ms(id) { + let dur = u8::try_from(dur / 4).unwrap_or(0); + buf[0] = dur; + Some(InResponse::Accepted(&buf[0..1])) + } else { + Some(InResponse::Rejected) + } + } else { + Some(InResponse::Rejected) + } + } + HID_REQ_GET_PROTOCOL => { + // UNSUPPORTED: Boot Protocol + buf[0] = 1; + Some(InResponse::Accepted(&buf[0..1])) + } + _ => Some(InResponse::Rejected), + } } - _ => InResponse::Rejected, + _ => None, } } } diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 39b499f0..ceccfd85 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -2,7 +2,6 @@ use core::mem; use crate::driver::Direction; -use crate::types::StringIndex; /// Control request type. #[repr(u8)] @@ -145,60 +144,3 @@ pub enum InResponse<'a> { /// The request was rejected. Rejected, } - -/// Handler for control requests. -/// -/// All methods are optional callbacks that will be called by -/// [`UsbDevice::run()`](crate::UsbDevice::run) -pub trait ControlHandler { - /// Called after a USB reset after the bus reset sequence is complete. - fn reset(&mut self) {} - - /// Called when a "set alternate setting" control request is done on the interface. - fn set_alternate_setting(&mut self, alternate_setting: u8) { - let _ = alternate_setting; - } - - /// Called when a control request is received with direction HostToDevice. - /// - /// # Arguments - /// - /// * `req` - The request from the SETUP packet. - /// * `data` - The data from the request. - fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { - let _ = (req, data); - OutResponse::Rejected - } - - /// Called when a control request is received with direction DeviceToHost. - /// - /// You should write the response somewhere (usually to `buf`, but you may use another buffer - /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. - /// - /// # Arguments - /// - /// * `req` - The request from the SETUP packet. - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { - let _ = (req, buf); - InResponse::Rejected - } - - /// Called when a GET DESCRIPTOR control request is received on the interface. - /// - /// You should write the response somewhere (usually to `buf`, but you may use another buffer - /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. - /// - /// # Arguments - /// - /// * `req` - The request from the SETUP packet. - fn get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { - let _ = (req, buf); - InResponse::Rejected - } - - /// Called when a GET_DESCRIPTOR STRING control request is received. - fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> { - let _ = (index, lang_id); - None - } -} diff --git a/embassy-usb/src/descriptor_reader.rs b/embassy-usb/src/descriptor_reader.rs index d64bcb73..05adcce6 100644 --- a/embassy-usb/src/descriptor_reader.rs +++ b/embassy-usb/src/descriptor_reader.rs @@ -1,5 +1,6 @@ use crate::descriptor::descriptor_type; use crate::driver::EndpointAddress; +use crate::types::InterfaceNumber; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -75,7 +76,7 @@ impl<'a, 'b> Iterator for DescriptorIter<'a, 'b> { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointInfo { pub configuration: u8, - pub interface: u8, + pub interface: InterfaceNumber, pub interface_alt: u8, pub ep_address: EndpointAddress, } @@ -83,7 +84,7 @@ pub struct EndpointInfo { pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> { let mut ep = EndpointInfo { configuration: 0, - interface: 0, + interface: InterfaceNumber(0), interface_alt: 0, ep_address: EndpointAddress::from(0), }; @@ -96,7 +97,7 @@ pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result< ep.configuration = r.read_u8()?; } descriptor_type::INTERFACE => { - ep.interface = r.read_u8()?; + ep.interface = InterfaceNumber(r.read_u8()?); ep.interface_alt = r.read_u8()?; } descriptor_type::ENDPOINT => { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index f8983318..bfeccd5f 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -82,32 +82,87 @@ const STRING_INDEX_PRODUCT: u8 = 2; const STRING_INDEX_SERIAL_NUMBER: u8 = 3; const STRING_INDEX_CUSTOM_START: u8 = 4; -/// A handler trait for changes in the device state of the [UsbDevice]. -pub trait DeviceStateHandler { +/// Handler for device events and control requests. +/// +/// All methods are optional callbacks that will be called by +/// [`UsbDevice::run()`](crate::UsbDevice::run) +pub trait Handler { /// Called when the USB device has been enabled or disabled. - fn enabled(&self, _enabled: bool) {} + fn enabled(&mut self, _enabled: bool) {} - /// Called when the host resets the device. - fn reset(&self) {} + /// Called after a USB reset after the bus reset sequence is complete. + fn reset(&mut self) {} /// Called when the host has set the address of the device to `addr`. - fn addressed(&self, _addr: u8) {} + fn addressed(&mut self, _addr: u8) {} /// Called when the host has enabled or disabled the configuration of the device. - fn configured(&self, _configured: bool) {} + fn configured(&mut self, _configured: bool) {} /// Called when the bus has entered or exited the suspend state. - fn suspended(&self, _suspended: bool) {} + fn suspended(&mut self, _suspended: bool) {} /// Called when remote wakeup feature is enabled or disabled. - fn remote_wakeup_enabled(&self, _enabled: bool) {} + fn remote_wakeup_enabled(&mut self, _enabled: bool) {} + + /// Called when a "set alternate setting" control request is done on the interface. + fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { + let _ = iface; + let _ = alternate_setting; + } + + /// Called when a control request is received with direction HostToDevice. + /// + /// # Arguments + /// + /// * `req` - The request from the SETUP packet. + /// * `data` - The data from the request. + /// + /// # Returns + /// + /// If you didn't handle this request (for example if it's for the wrong interface), return + /// `None`. In this case, the the USB stack will continue calling the other handlers, to see + /// if another handles it. + /// + /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack + /// respond to the control request, and stop calling other handlers. + fn control_out(&mut self, req: Request, data: &[u8]) -> Option { + let _ = (req, data); + None + } + + /// Called when a control request is received with direction DeviceToHost. + /// + /// You should write the response somewhere (usually to `buf`, but you may use another buffer + /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. + /// + /// # Arguments + /// + /// * `req` - The request from the SETUP packet. + /// + /// # Returns + /// + /// If you didn't handle this request (for example if it's for the wrong interface), return + /// `None`. In this case, the the USB stack will continue calling the other handlers, to see + /// if another handles it. + /// + /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack + /// respond to the control request, and stop calling other handlers. + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + let _ = (req, buf); + None + } + + /// Called when a GET_DESCRIPTOR STRING control request is received. + fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> { + let _ = (index, lang_id); + None + } } -struct Interface<'d> { - handler: Option<&'d mut dyn ControlHandler>, +struct Interface { current_alt_setting: u8, num_alt_settings: u8, - num_strings: u8, } /// Main struct for the USB device stack. @@ -119,7 +174,6 @@ pub struct UsbDevice<'d, D: Driver<'d>> { struct Inner<'d, D: Driver<'d>> { bus: D::Bus, - handler: Option<&'d dyn DeviceStateHandler>, config: Config<'d>, device_descriptor: &'d [u8], @@ -138,7 +192,9 @@ struct Inner<'d, D: Driver<'d>> { /// instead of regular `accept()`. set_address_pending: bool, - interfaces: Vec, MAX_INTERFACE_COUNT>, + interfaces: Vec, + handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, + #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, } @@ -147,11 +203,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { pub(crate) fn build( driver: D, config: Config<'d>, - handler: Option<&'d dyn DeviceStateHandler>, + handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, device_descriptor: &'d [u8], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], - interfaces: Vec, MAX_INTERFACE_COUNT>, + interfaces: Vec, control_buf: &'d mut [u8], #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, ) -> UsbDevice<'d, D> { @@ -165,7 +221,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { inner: Inner { bus, config, - handler, device_descriptor, config_descriptor, bos_descriptor, @@ -177,6 +232,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + handlers, #[cfg(feature = "msos-descriptor")] msos_descriptor, }, @@ -221,7 +277,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { self.inner.suspended = false; self.inner.remote_wakeup_enabled = false; - if let Some(h) = &self.inner.handler { + for h in &mut self.inner.handlers { h.enabled(false); } } @@ -250,7 +306,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { self.inner.bus.remote_wakeup().await?; self.inner.suspended = false; - if let Some(h) = &self.inner.handler { + for h in &mut self.inner.handlers { h.suspended(false); } @@ -361,29 +417,29 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.remote_wakeup_enabled = false; self.address = 0; - for iface in self.interfaces.iter_mut() { - iface.current_alt_setting = 0; - if let Some(h) = &mut iface.handler { - h.reset(); - h.set_alternate_setting(0); - } + for h in &mut self.handlers { + h.reset(); } - if let Some(h) = &self.handler { - h.reset(); + for (i, iface) in self.interfaces.iter_mut().enumerate() { + iface.current_alt_setting = 0; + + for h in &mut self.handlers { + h.set_alternate_setting(InterfaceNumber::new(i as _), 0); + } } } Event::Resume => { trace!("usb: resume"); self.suspended = false; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.suspended(false); } } Event::Suspend => { trace!("usb: suspend"); self.suspended = true; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.suspended(true); } } @@ -392,7 +448,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.bus.enable().await; self.device_state = UsbDeviceState::Default; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.enabled(true); } } @@ -401,7 +457,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.bus.disable().await; self.device_state = UsbDeviceState::Unpowered; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.enabled(false); } } @@ -416,14 +472,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { (RequestType::Standard, Recipient::Device) => match (req.request, req.value) { (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { self.remote_wakeup_enabled = false; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.remote_wakeup_enabled(false); } OutResponse::Accepted } (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { self.remote_wakeup_enabled = true; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.remote_wakeup_enabled(true); } OutResponse::Accepted @@ -432,7 +488,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.address = addr as u8; self.set_address_pending = true; self.device_state = UsbDeviceState::Addressed; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.addressed(self.address); } OutResponse::Accepted @@ -443,14 +499,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { // Enable all endpoints of selected alt settings. foreach_endpoint(self.config_descriptor, |ep| { - let iface = &self.interfaces[ep.interface as usize]; + let iface = &self.interfaces[ep.interface.0 as usize]; self.bus .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); }) .unwrap(); - // Notify handler. - if let Some(h) = &self.handler { + // Notify handlers. + for h in &mut self.handlers { h.configured(true); } @@ -468,8 +524,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { }) .unwrap(); - // Notify handler. - if let Some(h) = &self.handler { + // Notify handlers. + for h in &mut self.handlers { h.configured(false); } @@ -479,7 +535,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { _ => OutResponse::Rejected, }, (RequestType::Standard, Recipient::Interface) => { - let iface = match self.interfaces.get_mut(req.index as usize) { + let iface_num = InterfaceNumber::new(req.index as _); + let iface = match self.interfaces.get_mut(iface_num.0 as usize) { Some(iface) => iface, None => return OutResponse::Rejected, }; @@ -497,7 +554,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { // Enable/disable EPs of this interface as needed. foreach_endpoint(self.config_descriptor, |ep| { - if ep.interface == req.index as u8 { + if ep.interface == iface_num { self.bus .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); } @@ -506,8 +563,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { // TODO check it is valid (not out of range) - if let Some(handler) = &mut iface.handler { - handler.set_alternate_setting(new_altsetting); + for h in &mut self.handlers { + h.set_alternate_setting(iface_num, new_altsetting); } OutResponse::Accepted } @@ -527,17 +584,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { } _ => OutResponse::Rejected, }, - (RequestType::Class, Recipient::Interface) => { - let iface = match self.interfaces.get_mut(req.index as usize) { - Some(iface) => iface, - None => return OutResponse::Rejected, - }; - match &mut iface.handler { - Some(handler) => handler.control_out(req, data), - None => OutResponse::Rejected, - } - } - _ => OutResponse::Rejected, + _ => self.handle_control_out_delegated(req, data), } } @@ -582,11 +629,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { buf[0] = iface.current_alt_setting; InResponse::Accepted(&buf[..1]) } - Request::GET_DESCRIPTOR => match &mut iface.handler { - Some(handler) => handler.get_descriptor(req, buf), - None => InResponse::Rejected, - }, - _ => InResponse::Rejected, + _ => self.handle_control_in_delegated(req, buf), } } (RequestType::Standard, Recipient::Endpoint) => match req.request { @@ -601,34 +644,48 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { } _ => InResponse::Rejected, }, - (RequestType::Class, Recipient::Interface) => { - let iface = match self.interfaces.get_mut(req.index as usize) { - Some(iface) => iface, - None => return InResponse::Rejected, - }; - - match &mut iface.handler { - Some(handler) => handler.control_in(req, buf), - None => InResponse::Rejected, - } - } #[cfg(feature = "msos-descriptor")] (RequestType::Vendor, Recipient::Device) => { - if !self.msos_descriptor.is_empty() { - if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { - // Index 7 retrieves the MS OS Descriptor Set - InResponse::Accepted(self.msos_descriptor.descriptor()) - } else { - InResponse::Rejected - } + if !self.msos_descriptor.is_empty() + && req.request == self.msos_descriptor.vendor_code() + && req.index == 7 + { + // Index 7 retrieves the MS OS Descriptor Set + InResponse::Accepted(self.msos_descriptor.descriptor()) } else { - InResponse::Rejected + self.handle_control_in_delegated(req, buf) } } - _ => InResponse::Rejected, + _ => self.handle_control_in_delegated(req, buf), } } + fn handle_control_out_delegated(&mut self, req: Request, data: &[u8]) -> OutResponse { + for h in &mut self.handlers { + if let Some(res) = h.control_out(req, data) { + return res; + } + } + OutResponse::Rejected + } + + fn handle_control_in_delegated<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { + unsafe fn extend_lifetime<'x, 'y>(r: InResponse<'x>) -> InResponse<'y> { + core::mem::transmute(r) + } + + for h in &mut self.handlers { + if let Some(res) = h.control_in(req, buf) { + // safety: the borrow checker isn't smart enough to know this pattern (returning a + // borrowed value from inside the loop) is sound. Workaround by unsafely extending lifetime. + // Also, Polonius (the WIP new borrow checker) does accept it. + + return unsafe { extend_lifetime(res) }; + } + } + InResponse::Rejected + } + fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { let (dtype, index) = req.descriptor_type_index(); @@ -649,30 +706,16 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { STRING_INDEX_PRODUCT => self.config.product, STRING_INDEX_SERIAL_NUMBER => self.config.serial_number, _ => { - // Find out which iface owns this string index. - let mut index_left = index - STRING_INDEX_CUSTOM_START; - let mut the_iface = None; - for iface in &mut self.interfaces { - if index_left < iface.num_strings { - the_iface = Some(iface); + let mut s = None; + for handler in &mut self.handlers { + let index = StringIndex::new(index); + let lang_id = req.index; + if let Some(res) = handler.get_string(index, lang_id) { + s = Some(res); break; } - index_left -= iface.num_strings; - } - - if let Some(iface) = the_iface { - if let Some(handler) = &mut iface.handler { - let index = StringIndex::new(index); - let lang_id = req.index; - handler.get_string(index, lang_id) - } else { - warn!("String requested to an interface with no handler."); - None - } - } else { - warn!("String requested but didn't match to an interface."); - None } + s } }; diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index 1743e61f..15d19500 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,9 +1,9 @@ //! USB types. /// A handle for a USB interface that contains its number. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InterfaceNumber(pub(crate) u8); +pub struct InterfaceNumber(pub u8); impl InterfaceNumber { pub(crate) fn new(index: u8) -> InterfaceNumber { @@ -20,7 +20,7 @@ impl From for u8 { /// A handle for a USB string descriptor that contains its index. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct StringIndex(u8); +pub struct StringIndex(pub u8); impl StringIndex { pub(crate) fn new(index: u8) -> StringIndex { diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 699666ce..97978089 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -82,7 +82,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 017cac19..3d8a114c 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -16,7 +16,7 @@ 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::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) { let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; - let device_state_handler = MyDeviceStateHandler::new(); + let mut device_handler = MyDeviceHandler::new(); let mut state = State::new(); @@ -63,9 +63,10 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - Some(&device_state_handler), ); + builder.handler(&mut device_handler); + // Create classes on the builder. let config = embassy_usb::class::hid::Config { report_descriptor: KeyboardReport::desc(), @@ -164,20 +165,20 @@ impl RequestHandler for MyRequestHandler { } } -struct MyDeviceStateHandler { +struct MyDeviceHandler { configured: AtomicBool, } -impl MyDeviceStateHandler { +impl MyDeviceHandler { fn new() -> Self { - MyDeviceStateHandler { + MyDeviceHandler { configured: AtomicBool::new(false), } } } -impl DeviceStateHandler for MyDeviceStateHandler { - fn enabled(&self, enabled: bool) { +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { self.configured.store(false, Ordering::Relaxed); SUSPENDED.store(false, Ordering::Release); if enabled { @@ -187,17 +188,17 @@ impl DeviceStateHandler for MyDeviceStateHandler { } } - fn reset(&self) { + fn reset(&mut self) { self.configured.store(false, Ordering::Relaxed); info!("Bus reset, the Vbus current limit is 100mA"); } - fn addressed(&self, addr: u8) { + fn addressed(&mut self, addr: u8) { self.configured.store(false, Ordering::Relaxed); info!("USB address set to: {}", addr); } - fn configured(&self, configured: bool) { + fn configured(&mut self, configured: bool) { self.configured.store(configured, Ordering::Relaxed); if configured { info!("Device configured, it may now draw up to the configured current limit from Vbus.") @@ -206,7 +207,7 @@ impl DeviceStateHandler for MyDeviceStateHandler { } } - fn suspended(&self, suspended: bool) { + fn suspended(&mut self, suspended: bool) { if suspended { info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); SUSPENDED.store(true, Ordering::Release); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index a5849129..d7c9d55b 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 18b6f25b..102d7ea6 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 3532d3f8..558d4ba6 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -83,7 +83,6 @@ async fn main(spawner: Spawner) { &mut res.config_descriptor, &mut res.bos_descriptor, &mut res.control_buf, - None, ); // Create classes on the builder. diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index f4b828de..ade6af52 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -65,7 +65,6 @@ async fn main(_spawner: Spawner) { &mut bos_descriptor, &mut msos_descriptor, &mut control_buf, - None, ); builder.msos_descriptor(windows_version::WIN8_1, 2); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 104b25d3..66a6ed4d 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -73,7 +73,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index b7d6493b..a991082e 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -53,7 +53,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index ad92cdeb..07cad84e 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index f6d27c86..5b4e0a91 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index cf2885ae..4a16aac0 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -82,7 +82,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 01464776..baabc1a2 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 688bd0da..5fd9d2ec 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index b319d12c..9ef520ae 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 3e38b10a..663f60d5 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index e5a46b06..98ec0e83 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -79,7 +79,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index d38ed749..e3bbe9d0 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -51,7 +51,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7562a4e9..66ccacb7 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index c846836b..8cd3bf2f 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. From 86487db5d1773d2a764ab340051d70cfa40e4714 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 23:45:01 +0100 Subject: [PATCH 343/374] usb: use InterfaceNumber in msos. --- embassy-usb/src/builder.rs | 2 +- embassy-usb/src/msos.rs | 7 ++++--- embassy-usb/src/types.rs | 2 ++ examples/nrf52840/src/bin/usb_serial_winusb.rs | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 07b2a177..305dfa02 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -346,7 +346,7 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { } if !self.builder.msos_descriptor.is_in_function_subset() { - self.builder.msos_descriptor.function(self.first_interface.0); + self.builder.msos_descriptor.function(self.first_interface); } #[cfg(feature = "msos-descriptor")] diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 19ed3497..b1e0335e 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -7,6 +7,7 @@ use core::mem::size_of; use super::{capability_type, BosWriter}; +use crate::types::InterfaceNumber; /// A serialized Microsoft OS 2.0 Descriptor set. /// @@ -125,7 +126,7 @@ impl<'d> MsOsDescriptorWriter<'d> { } /// Add a function subset. - pub fn function(&mut self, first_interface: u8) { + pub fn function(&mut self, first_interface: InterfaceNumber) { assert!( self.config_mark.is_some(), "MsOsDescriptorWriter: function subset requires a configuration subset" @@ -376,14 +377,14 @@ impl DescriptorSet for ConfigurationSubsetHeader { pub struct FunctionSubsetHeader { wLength: u16, wDescriptorType: u16, - bFirstInterface: u8, + bFirstInterface: InterfaceNumber, bReserved: u8, wSubsetLength: u16, } impl FunctionSubsetHeader { /// Creates a function subset header - pub fn new(first_interface: u8) -> Self { + pub fn new(first_interface: InterfaceNumber) -> Self { FunctionSubsetHeader { wLength: (size_of::() as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(), diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index 15d19500..c7a47f7e 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -3,6 +3,7 @@ /// A handle for a USB interface that contains its number. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] pub struct InterfaceNumber(pub u8); impl InterfaceNumber { @@ -20,6 +21,7 @@ impl From for u8 { /// A handle for a USB string descriptor that contains its index. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] pub struct StringIndex(pub u8); impl StringIndex { diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index ade6af52..6561fc3b 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -12,6 +12,7 @@ use embassy_nrf::{interrupt, pac}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::msos::{self, windows_version}; +use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -77,7 +78,7 @@ async fn main(_spawner: Spawner) { // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. let msos_writer = builder.msos_writer(); msos_writer.configuration(0); - msos_writer.function(0); + msos_writer.function(InterfaceNumber(0)); msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( "DeviceInterfaceGUIDs", From da6b1e8399cf977698e4a630f0862f698cba2b83 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Wed, 8 Feb 2023 17:52:49 +0200 Subject: [PATCH 344/374] Reset rng in case of clock or seed error --- embassy-stm32/src/rng.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 10fc4a75..c487b759 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -90,8 +90,10 @@ impl<'d, T: Instance> Rng<'d, T> { impl<'d, T: Instance> RngCore for Rng<'d, T> { fn next_u32(&mut self) -> u32 { loop { - let bits = unsafe { T::regs().sr().read() }; - if bits.drdy() { + let sr = unsafe { T::regs().sr().read() }; + if sr.seis() | sr.ceis() { + self.reset(); + } else if sr.drdy() { return unsafe { T::regs().dr().read() }; } } From bab4277a8620e9a5e2347ff919d677e9c87528c9 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Wed, 8 Feb 2023 17:57:37 +0200 Subject: [PATCH 345/374] hack for STM32WL, rcc reset in case of seed error The STM32WL series has a more complicated rng device that gets stuck when there is a seed error. --- embassy-stm32/src/rng.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index c487b759..abc62583 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -32,6 +32,11 @@ impl<'d, T: Instance> Rng<'d, T> { } pub fn reset(&mut self) { + //stm32wl gets stuck if there is a seed error + #[cfg(stm32wl)] + if unsafe { T::regs().sr().read().seis()} { + T::reset(); + } unsafe { T::regs().cr().modify(|reg| { reg.set_rngen(true); From ac3e225988f1666eaa773a20561cbf40a3295613 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Feb 2023 17:12:22 +0100 Subject: [PATCH 346/374] Update Rust nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f2adc435..da75fa53 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-11-22" +channel = "nightly-2023-02-07" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 5edb3052e6e7c26e8b936ca1633e32d18c4bd67c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Feb 2023 17:52:02 +0100 Subject: [PATCH 347/374] net: reexport driver crate. --- embassy-net/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 8d0119f6..0f694ee7 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -8,7 +8,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub mod device; +pub use embassy_net_driver as driver; + +mod device; #[cfg(feature = "tcp")] pub mod tcp; #[cfg(feature = "udp")] From ab4b3fa96dc320f034f5437c9540897f553519be Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 9 Feb 2023 12:42:57 +0200 Subject: [PATCH 348/374] update stm32-data to include rng_v2 --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index cc93f9d1..66252982 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit cc93f9d10395077770bebefb6b9488e06b0e5811 +Subproject commit 66252982939014e94fc4a1b7423c30c3d108ae0b From 43d018b67ff98a68e7d1d8c122f803a3d90c9381 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 9 Feb 2023 12:44:20 +0200 Subject: [PATCH 349/374] Use rng_v2 cfg instead of chip specific for seed error recover hack --- embassy-stm32/src/rng.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index abc62583..cd3cc94c 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -32,8 +32,7 @@ impl<'d, T: Instance> Rng<'d, T> { } pub fn reset(&mut self) { - //stm32wl gets stuck if there is a seed error - #[cfg(stm32wl)] + #[cfg(rng_v2)] if unsafe { T::regs().sr().read().seis()} { T::reset(); } From 2b6654541d2d195636cf7a9c5ef3fdced0583e21 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 9 Feb 2023 13:01:44 +0200 Subject: [PATCH 350/374] rustfmt --- embassy-stm32/src/rng.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index cd3cc94c..1e16b847 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -32,8 +32,9 @@ impl<'d, T: Instance> Rng<'d, T> { } pub fn reset(&mut self) { + // rng_v2 locks up on seed error, needs reset #[cfg(rng_v2)] - if unsafe { T::regs().sr().read().seis()} { + if unsafe { T::regs().sr().read().seis() } { T::reset(); } unsafe { From a4371e9544f284927aae3b0de0ff5318f81acc80 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 9 Feb 2023 19:22:06 -0500 Subject: [PATCH 351/374] Add from_hz function for Duration. --- embassy-time/src/duration.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index d3c6f42a..846a9c3d 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -81,6 +81,11 @@ impl Duration { } } + /// Creates a duration corresponding to the specified Hz. + pub const fn from_hz(hz: u64) -> Duration { + Duration { ticks: TICK_HZ / hz } + } + /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_add(self, rhs: Duration) -> Option { self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks }) From bd7b3bd455fc5946a3944bd931acfdf255929cb6 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 9 Feb 2023 20:57:27 -0500 Subject: [PATCH 352/374] Clamp ticks to 1 and round to nearest. --- embassy-time/src/duration.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index 846a9c3d..9d0bab2d 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -82,8 +82,17 @@ impl Duration { } /// Creates a duration corresponding to the specified Hz. + /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1 + /// tick. Doing so will not deadlock, but will certainly not produce the desired output. pub const fn from_hz(hz: u64) -> Duration { - Duration { ticks: TICK_HZ / hz } + let ticks = { + if hz >= TICK_HZ { + 1 + } else { + (TICK_HZ + hz / 2) / hz + } + }; + Duration { ticks } } /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. From 9cfea693edec5af17ba698f64b3f0a168ad92944 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 31 Jan 2023 22:06:41 +0100 Subject: [PATCH 353/374] Add DNS socket to embassy-net --- embassy-net/Cargo.toml | 4 +- embassy-net/src/dns.rs | 114 ++++++++++++++++++++++++++++++++ embassy-net/src/lib.rs | 2 + examples/std/Cargo.toml | 2 +- examples/std/src/bin/net_dns.rs | 102 ++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 embassy-net/src/dns.rs create mode 100644 examples/std/src/bin/net_dns.rs diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 4ec340b7..6b346828 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] default = [] -std = [] +std = ["smoltcp/alloc", "managed/std"] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] @@ -22,7 +22,7 @@ unstable-traits = [] udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] -dns = ["smoltcp/socket-dns"] +dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"] dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs new file mode 100644 index 00000000..f18750cc --- /dev/null +++ b/embassy-net/src/dns.rs @@ -0,0 +1,114 @@ +//! DNS socket with async support. +use core::cell::RefCell; +use core::future::poll_fn; +use core::mem; +use core::task::Poll; + +use embassy_net_driver::Driver; +use heapless::Vec; +use managed::ManagedSlice; +use smoltcp::iface::{Interface, SocketHandle}; +pub use smoltcp::socket::dns::DnsQuery; +use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; +pub use smoltcp::wire::{DnsQueryType, IpAddress}; + +use crate::{SocketStack, Stack}; + +/// Errors returned by DnsSocket. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// No available query slot + NoFreeSlot, + /// Invalid name + InvalidName, + /// Name too long + NameTooLong, + /// Name lookup failed + Failed, +} + +impl From for Error { + fn from(_: GetQueryResultError) -> Self { + Self::Failed + } +} + +impl From for Error { + fn from(e: StartQueryError) -> Self { + match e { + StartQueryError::NoFreeSlot => Self::NoFreeSlot, + StartQueryError::InvalidName => Self::InvalidName, + StartQueryError::NameTooLong => Self::NameTooLong, + } + } +} + +/// Async socket for making DNS queries. +pub struct DnsSocket<'a> { + stack: &'a RefCell, + handle: SocketHandle, +} + +impl<'a> DnsSocket<'a> { + /// Create a new DNS socket using the provided stack and query storage. + /// + /// DNS servers are derived from the stack configuration. + /// + /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. + pub fn new(stack: &'a Stack, queries: Q) -> Self + where + D: Driver + 'static, + Q: Into>>, + { + let servers = stack + .config() + .map(|c| { + let v: Vec = c.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + v + }) + .unwrap_or(Vec::new()); + let s = &mut *stack.socket.borrow_mut(); + let queries: ManagedSlice<'static, Option> = unsafe { mem::transmute(queries.into()) }; + + let handle = s.sockets.add(dns::Socket::new(&servers[..], queries)); + Self { + stack: &stack.socket, + handle, + } + } + + fn with_mut(&mut self, f: impl FnOnce(&mut dns::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); + let socket = s.sockets.get_mut::(self.handle); + let res = f(socket, &mut s.iface); + s.waker.wake(); + res + } + + /// Make a query for a given name and return the corresponding IP addresses. + pub async fn query(&mut self, name: &str, qtype: DnsQueryType) -> Result, Error> { + let query = match { self.with_mut(|s, i| s.start_query(i.context(), name, qtype)) } { + Ok(handle) => handle, + Err(e) => return Err(e.into()), + }; + + poll_fn(|cx| { + self.with_mut(|s, _| match s.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(GetQueryResultError::Pending) => { + s.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e.into())), + }) + }) + .await + } +} + +impl<'a> Drop for DnsSocket<'a> { + fn drop(&mut self) { + self.stack.borrow_mut().sockets.remove(self.handle); + } +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 0f694ee7..ae447d06 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -11,6 +11,8 @@ pub(crate) mod fmt; pub use embassy_net_driver as driver; mod device; +#[cfg(feature = "dns")] +pub mod dns; #[cfg(feature = "tcp")] pub mod tcp; #[cfg(feature = "udp")] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index af1481e0..8087df09 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs new file mode 100644 index 00000000..6203f837 --- /dev/null +++ b/examples/std/src/bin/net_dns.rs @@ -0,0 +1,102 @@ +#![feature(type_alias_impl_trait)] + +use std::default::Default; + +use clap::Parser; +use embassy_executor::{Executor, Spawner}; +use embassy_net::dns::{DnsQueryType, DnsSocket}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use heapless::Vec; +use log::*; +use rand_core::{OsRng, RngCore}; +use static_cell::StaticCell; + +#[path = "../tuntap.rs"] +mod tuntap; + +use crate::tuntap::TunTapDevice; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[derive(Parser)] +#[clap(version = "1.0")] +struct Opts { + /// TAP device name + #[clap(long, default_value = "tap0")] + tap: String, + /// use a static IP instead of DHCP + #[clap(long)] + static_ip: bool, +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + + // Init network device + let device = TunTapDevice::new(&opts.tap).unwrap(); + + // Choose between dhcp or static ip + let config = if opts.static_ip { + Config::Static(embassy_net::StaticConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) + .unwrap(), + gateway: Some(Ipv4Address::new(192, 168, 69, 100)), + }) + } else { + Config::Dhcp(Default::default()) + }; + + // Generate random seed + let mut seed = [0; 8]; + OsRng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + + // Launch network task + spawner.spawn(net_task(stack)).unwrap(); + + // Then we can use it! + + let mut socket = DnsSocket::new(stack, vec![]); + + let host = "example.com"; + info!("querying host {:?}...", host); + match socket.query(host, DnsQueryType::A).await { + Ok(r) => { + info!("query response: {:?}", r); + } + Err(e) => { + warn!("query error: {:?}", e); + } + }; +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} From c203cefe0103ae150be514875b6b110b1f9a3a90 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 6 Feb 2023 20:18:12 +0100 Subject: [PATCH 354/374] Handle cancellation --- embassy-net/Cargo.toml | 1 + embassy-net/src/dns.rs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6b346828..6eea8c30 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -40,6 +40,7 @@ smoltcp = { version = "0.9.0", default-features = false, features = [ ]} embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index f18750cc..2d2e7b79 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -11,6 +11,7 @@ use smoltcp::iface::{Interface, SocketHandle}; pub use smoltcp::socket::dns::DnsQuery; use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; +use embassy_hal_common::drop::OnDrop; use crate::{SocketStack, Stack}; @@ -93,7 +94,15 @@ impl<'a> DnsSocket<'a> { Err(e) => return Err(e.into()), }; - poll_fn(|cx| { + let handle = self.handle; + let drop = OnDrop::new(|| { + let s = &mut *self.stack.borrow_mut(); + let socket = s.sockets.get_mut::(handle); + socket.cancel_query(query); + s.waker.wake(); + }); + + let res = poll_fn(|cx| { self.with_mut(|s, _| match s.get_query_result(query) { Ok(addrs) => Poll::Ready(Ok(addrs)), Err(GetQueryResultError::Pending) => { @@ -103,7 +112,10 @@ impl<'a> DnsSocket<'a> { Err(e) => Poll::Ready(Err(e.into())), }) }) - .await + .await; + + drop.defuse(); + res } } From 614740a1b2ea1cada16e3391cb86d0394f64edfc Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 6 Feb 2023 20:36:08 +0100 Subject: [PATCH 355/374] cargo fmt --- embassy-net/src/dns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 2d2e7b79..e98247bf 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -4,6 +4,7 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_hal_common::drop::OnDrop; use embassy_net_driver::Driver; use heapless::Vec; use managed::ManagedSlice; @@ -11,7 +12,6 @@ use smoltcp::iface::{Interface, SocketHandle}; pub use smoltcp::socket::dns::DnsQuery; use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use embassy_hal_common::drop::OnDrop; use crate::{SocketStack, Stack}; From cd440a49d677f7dfc09e405d99b87a49fba9ba31 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 17:43:23 +0100 Subject: [PATCH 356/374] Rewrite to use a single socket --- embassy-net/Cargo.toml | 2 +- embassy-net/src/dns.rs | 124 +++++++++++++------------------- embassy-net/src/lib.rs | 83 ++++++++++++++++++++- examples/std/src/bin/net_dns.rs | 3 +- 4 files changed, 133 insertions(+), 79 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6eea8c30..53778899 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -52,5 +52,5 @@ generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" -embedded-nal-async = { version = "0.3.0", optional = true } +embedded-nal-async = { version = "0.4.0", optional = true } atomic-polyfill = { version = "1.0" } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index e98247bf..1815d258 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,19 +1,10 @@ //! DNS socket with async support. -use core::cell::RefCell; -use core::future::poll_fn; -use core::mem; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; -use embassy_net_driver::Driver; use heapless::Vec; -use managed::ManagedSlice; -use smoltcp::iface::{Interface, SocketHandle}; -pub use smoltcp::socket::dns::DnsQuery; -use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; +pub use smoltcp::socket::dns::{DnsQuery, Socket, MAX_ADDRESS_COUNT}; +pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use crate::{SocketStack, Stack}; +use crate::{Driver, Stack}; /// Errors returned by DnsSocket. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -46,81 +37,64 @@ impl From for Error { } /// Async socket for making DNS queries. -pub struct DnsSocket<'a> { - stack: &'a RefCell, - handle: SocketHandle, +pub struct DnsSocket<'a, D> +where + D: Driver + 'static, +{ + stack: &'a Stack, } -impl<'a> DnsSocket<'a> { +impl<'a, D> DnsSocket<'a, D> +where + D: Driver + 'static, +{ /// Create a new DNS socket using the provided stack and query storage. /// /// DNS servers are derived from the stack configuration. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. - pub fn new(stack: &'a Stack, queries: Q) -> Self - where - D: Driver + 'static, - Q: Into>>, - { - let servers = stack - .config() - .map(|c| { - let v: Vec = c.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - v - }) - .unwrap_or(Vec::new()); - let s = &mut *stack.socket.borrow_mut(); - let queries: ManagedSlice<'static, Option> = unsafe { mem::transmute(queries.into()) }; - - let handle = s.sockets.add(dns::Socket::new(&servers[..], queries)); - Self { - stack: &stack.socket, - handle, - } - } - - fn with_mut(&mut self, f: impl FnOnce(&mut dns::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(self.handle); - let res = f(socket, &mut s.iface); - s.waker.wake(); - res + pub fn new(stack: &'a Stack) -> Self { + Self { stack } } /// Make a query for a given name and return the corresponding IP addresses. - pub async fn query(&mut self, name: &str, qtype: DnsQueryType) -> Result, Error> { - let query = match { self.with_mut(|s, i| s.start_query(i.context(), name, qtype)) } { - Ok(handle) => handle, - Err(e) => return Err(e.into()), + pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { + self.stack.dns_query(name, qtype).await + } +} + +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D> +where + D: Driver + 'static, +{ + type Error = Error; + + async fn get_host_by_name( + &self, + host: &str, + addr_type: embedded_nal_async::AddrType, + ) -> Result { + use embedded_nal_async::{AddrType, IpAddr}; + let qtype = match addr_type { + AddrType::IPv6 => DnsQueryType::Aaaa, + _ => DnsQueryType::A, }; - - let handle = self.handle; - let drop = OnDrop::new(|| { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(handle); - socket.cancel_query(query); - s.waker.wake(); - }); - - let res = poll_fn(|cx| { - self.with_mut(|s, _| match s.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), - Err(GetQueryResultError::Pending) => { - s.register_query_waker(query, cx.waker()); - Poll::Pending - } - Err(e) => Poll::Ready(Err(e.into())), + let addrs = self.query(host, qtype).await?; + if let Some(first) = addrs.get(0) { + Ok(match first { + IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), }) - }) - .await; + } else { + Err(Error::Failed) + } + } - drop.defuse(); - res - } -} - -impl<'a> Drop for DnsSocket<'a> { - fn drop(&mut self) { - self.stack.borrow_mut().sockets.remove(self.handle); + async fn get_host_by_address( + &self, + _addr: embedded_nal_async::IpAddr, + ) -> Result, Self::Error> { + todo!() } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ae447d06..b63aa83d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -48,15 +48,22 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; +const MAX_QUERIES: usize = 2; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], + #[cfg(feature = "dns")] + queries: Option<[Option; MAX_QUERIES]>, } impl StackResources { pub fn new() -> Self { + #[cfg(feature = "dns")] + const INIT: Option = None; Self { sockets: [SocketStorage::EMPTY; SOCK], + #[cfg(feature = "dns")] + queries: Some([INIT; MAX_QUERIES]), } } } @@ -109,6 +116,8 @@ struct Inner { config: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, + #[cfg(feature = "dns")] + dns_socket: Option, } pub(crate) struct SocketStack { @@ -153,6 +162,8 @@ impl Stack { config: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, + #[cfg(feature = "dns")] + dns_socket: None, }; let mut socket = SocketStack { sockets, @@ -161,8 +172,17 @@ impl Stack { next_local_port, }; + #[cfg(feature = "dns")] + { + if let Some(queries) = resources.queries.take() { + inner.dns_socket = Some(socket.sockets.add(dns::Socket::new(&[], queries))); + } + } + match config { - Config::Static(config) => inner.apply_config(&mut socket, config), + Config::Static(config) => { + inner.apply_config(&mut socket, config); + } #[cfg(feature = "dhcpv4")] Config::Dhcp(config) => { let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); @@ -210,6 +230,59 @@ impl Stack { .await; unreachable!() } + + #[cfg(feature = "dns")] + async fn dns_query( + &self, + name: &str, + qtype: dns::DnsQueryType, + ) -> Result, dns::Error> { + let query = self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + match socket.start_query(s.iface.context(), name, qtype) { + Ok(handle) => Ok(handle), + Err(e) => Err(e.into()), + } + } else { + Err(dns::Error::Failed) + } + })?; + + use embassy_hal_common::drop::OnDrop; + let drop = OnDrop::new(|| { + self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + socket.cancel_query(query); + s.waker.wake(); + } + }) + }); + + let res = poll_fn(|cx| { + self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + match socket.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(dns::GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e.into())), + } + } else { + Poll::Ready(Err(dns::Error::Failed)) + } + }) + }) + .await; + + drop.defuse(); + + res + } } impl SocketStack { @@ -251,6 +324,13 @@ impl Inner { debug!(" DNS server {}: {}", i, s); } + #[cfg(feature = "dns")] + if let Some(dns_socket) = self.dns_socket { + let socket = s.sockets.get_mut::(dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); + } + self.config = Some(config) } @@ -326,6 +406,7 @@ impl Inner { //if old_link_up || self.link_up { // self.poll_configurator(timestamp) //} + // if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { let t = Timer::at(instant_from_smoltcp(poll_at)); diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 6203f837..e787cb82 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -71,8 +71,7 @@ async fn main_task(spawner: Spawner) { spawner.spawn(net_task(stack)).unwrap(); // Then we can use it! - - let mut socket = DnsSocket::new(stack, vec![]); + let socket = DnsSocket::new(stack); let host = "example.com"; info!("querying host {:?}...", host); From 7ae47cb1d81d5112fdc42ceaec470350aab5c372 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:20:50 +0100 Subject: [PATCH 357/374] Expose api on Stack and add doc Make it work with smoltcp 0.9 --- embassy-net/src/dns.rs | 8 +++----- embassy-net/src/lib.rs | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 1815d258..9b1b936c 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,6 +1,6 @@ //! DNS socket with async support. use heapless::Vec; -pub use smoltcp::socket::dns::{DnsQuery, Socket, MAX_ADDRESS_COUNT}; +pub use smoltcp::socket::dns::{DnsQuery, Socket}; pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; @@ -48,9 +48,7 @@ impl<'a, D> DnsSocket<'a, D> where D: Driver + 'static, { - /// Create a new DNS socket using the provided stack and query storage. - /// - /// DNS servers are derived from the stack configuration. + /// Create a new DNS socket using the provided stack. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. pub fn new(stack: &'a Stack) -> Self { @@ -58,7 +56,7 @@ where } /// Make a query for a given name and return the corresponding IP addresses. - pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { + pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { self.stack.dns_query(name, qtype).await } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b63aa83d..5b6ab0e3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -231,12 +231,9 @@ impl Stack { unreachable!() } + /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] - async fn dns_query( - &self, - name: &str, - qtype: dns::DnsQueryType, - ) -> Result, dns::Error> { + pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { let query = self.with_mut(|s, i| { if let Some(dns_handle) = i.dns_socket { let socket = s.sockets.get_mut::(dns_handle); From 6e68353a931cc268f61b4606c3a99bf8f5e96f6e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:30:17 +0100 Subject: [PATCH 358/374] attempt removing option --- embassy-net/src/lib.rs | 76 ++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 5b6ab0e3..627c0a0f 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -53,7 +53,7 @@ const MAX_QUERIES: usize = 2; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], #[cfg(feature = "dns")] - queries: Option<[Option; MAX_QUERIES]>, + queries: [Option; MAX_QUERIES], } impl StackResources { @@ -63,7 +63,7 @@ impl StackResources { Self { sockets: [SocketStorage::EMPTY; SOCK], #[cfg(feature = "dns")] - queries: Some([INIT; MAX_QUERIES]), + queries: [INIT; MAX_QUERIES], } } } @@ -117,7 +117,7 @@ struct Inner { #[cfg(feature = "dhcpv4")] dhcp_socket: Option, #[cfg(feature = "dns")] - dns_socket: Option, + dns_socket: SocketHandle, } pub(crate) struct SocketStack { @@ -156,15 +156,7 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - let mut inner = Inner { - device, - link_up: false, - config: None, - #[cfg(feature = "dhcpv4")] - dhcp_socket: None, - #[cfg(feature = "dns")] - dns_socket: None, - }; + let mut socket = SocketStack { sockets, iface, @@ -172,12 +164,15 @@ impl Stack { next_local_port, }; - #[cfg(feature = "dns")] - { - if let Some(queries) = resources.queries.take() { - inner.dns_socket = Some(socket.sockets.add(dns::Socket::new(&[], queries))); - } - } + let mut inner = Inner { + device, + link_up: false, + config: None, + #[cfg(feature = "dhcpv4")] + dhcp_socket: None, + #[cfg(feature = "dns")] + dns_socket: socket.sockets.add(dns::Socket::new(&[], &mut resources.queries)), + }; match config { Config::Static(config) => { @@ -235,42 +230,29 @@ impl Stack { #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { let query = self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - match socket.start_query(s.iface.context(), name, qtype) { - Ok(handle) => Ok(handle), - Err(e) => Err(e.into()), - } - } else { - Err(dns::Error::Failed) - } + let socket = s.sockets.get_mut::(i.dns_socket); + socket.start_query(s.iface.context(), name, qtype) })?; use embassy_hal_common::drop::OnDrop; let drop = OnDrop::new(|| { self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - socket.cancel_query(query); - s.waker.wake(); - } + let socket = s.sockets.get_mut::(i.dns_socket); + socket.cancel_query(query); + s.waker.wake(); }) }); let res = poll_fn(|cx| { self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - match socket.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), - Err(dns::GetQueryResultError::Pending) => { - socket.register_query_waker(query, cx.waker()); - Poll::Pending - } - Err(e) => Poll::Ready(Err(e.into())), + let socket = s.sockets.get_mut::(i.dns_socket); + match socket.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(dns::GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending } - } else { - Poll::Ready(Err(dns::Error::Failed)) + Err(e) => Poll::Ready(Err(e.into())), } }) }) @@ -322,11 +304,9 @@ impl Inner { } #[cfg(feature = "dns")] - if let Some(dns_socket) = self.dns_socket { - let socket = s.sockets.get_mut::(dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); - } + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); self.config = Some(config) } From 472473d8c1cfc5985ee81fa8dac88c46b8fa64a9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:32:35 +0100 Subject: [PATCH 359/374] Create slice using ::Owned --- embassy-net/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 627c0a0f..25a7891c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -171,7 +171,7 @@ impl Stack { #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new(&[], &mut resources.queries)), + dns_socket: socket.sockets.add(dns::Socket::new(&[], managed::ManagedSlice::Borrowed(&mut resources.queries))), }; match config { From 48dff04d647a5303b9ed0a85ecce2f8a8f17ba0f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:34:21 +0100 Subject: [PATCH 360/374] Bump max queries --- embassy-net/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 25a7891c..e5f7479c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -48,7 +48,8 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -const MAX_QUERIES: usize = 2; +#[cfg(feature = "dns")] +const MAX_QUERIES: usize = 4; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], From 32c3725631b72807b926187d29916468fed68c81 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:44:51 +0100 Subject: [PATCH 361/374] add waker for DNS slots --- embassy-net/src/dns.rs | 4 +--- embassy-net/src/lib.rs | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 9b1b936c..2dd44a4e 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -10,8 +10,6 @@ use crate::{Driver, Stack}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// No available query slot - NoFreeSlot, /// Invalid name InvalidName, /// Name too long @@ -29,7 +27,7 @@ impl From for Error { impl From for Error { fn from(e: StartQueryError) -> Self { match e { - StartQueryError::NoFreeSlot => Self::NoFreeSlot, + StartQueryError::NoFreeSlot => Self::Failed, StartQueryError::InvalidName => Self::InvalidName, StartQueryError::NameTooLong => Self::NameTooLong, } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e5f7479c..9d9de913 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -119,6 +119,8 @@ struct Inner { dhcp_socket: Option, #[cfg(feature = "dns")] dns_socket: SocketHandle, + #[cfg(feature = "dns")] + dns_waker: WakerRegistration, } pub(crate) struct SocketStack { @@ -157,7 +159,6 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - let mut socket = SocketStack { sockets, iface, @@ -172,7 +173,12 @@ impl Stack { #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new(&[], managed::ManagedSlice::Borrowed(&mut resources.queries))), + dns_socket: socket.sockets.add(dns::Socket::new( + &[], + managed::ManagedSlice::Borrowed(&mut resources.queries), + )), + #[cfg(feature = "dns")] + dns_waker: WakerRegistration::new(), }; match config { @@ -230,10 +236,20 @@ impl Stack { /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { - let query = self.with_mut(|s, i| { - let socket = s.sockets.get_mut::(i.dns_socket); - socket.start_query(s.iface.context(), name, qtype) - })?; + let query = poll_fn(|cx| { + self.with_mut(|s, i| { + let socket = s.sockets.get_mut::(i.dns_socket); + match socket.start_query(s.iface.context(), name, qtype) { + Ok(handle) => Poll::Ready(Ok(handle)), + Err(dns::StartQueryError::NoFreeSlot) => { + i.dns_waker.register(cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e)), + } + }) + }) + .await?; use embassy_hal_common::drop::OnDrop; let drop = OnDrop::new(|| { @@ -241,6 +257,7 @@ impl Stack { let socket = s.sockets.get_mut::(i.dns_socket); socket.cancel_query(query); s.waker.wake(); + i.dns_waker.wake(); }) }); @@ -248,12 +265,18 @@ impl Stack { self.with_mut(|s, i| { let socket = s.sockets.get_mut::(i.dns_socket); match socket.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), + Ok(addrs) => { + i.dns_waker.wake(); + Poll::Ready(Ok(addrs)) + } Err(dns::GetQueryResultError::Pending) => { socket.register_query_waker(query, cx.waker()); Poll::Pending } - Err(e) => Poll::Ready(Err(e.into())), + Err(e) => { + i.dns_waker.wake(); + Poll::Ready(Err(e.into())) + } } }) }) From a7d3ef9122e45a808e1c401b33ee380a0c7c867d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:00:00 +0100 Subject: [PATCH 362/374] scope dns operations within a cfged block --- embassy-net/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 9d9de913..bda5f9e1 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -328,9 +328,11 @@ impl Inner { } #[cfg(feature = "dns")] - let socket = s.sockets.get_mut::(self.dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); + { + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); + } self.config = Some(config) } From 128a453163acbdd2f3e3e320bbd06eb38f196601 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:04:54 +0100 Subject: [PATCH 363/374] remove unneeded features --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 53778899..2d8c5c7b 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] default = [] -std = ["smoltcp/alloc", "managed/std"] +std = [] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] From a2b8921ff3e0b50da824c2242f0b90f9f8f9a0e2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:38:17 +0100 Subject: [PATCH 364/374] fix: cfg guard for ipv6 --- embassy-net/src/dns.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 2dd44a4e..97320e44 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -80,6 +80,7 @@ where if let Some(first) = addrs.get(0) { Ok(match first { IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), }) } else { From a509af4bc00ae6945e568b268731e854e8ae3994 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 23:00:16 +0100 Subject: [PATCH 365/374] exmaples/dns: don't use the socket. --- examples/std/src/bin/net_dns.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index e787cb82..e1cc45a3 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -4,7 +4,7 @@ use std::default::Default; use clap::Parser; use embassy_executor::{Executor, Spawner}; -use embassy_net::dns::{DnsQueryType, DnsSocket}; +use embassy_net::dns::DnsQueryType; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; @@ -65,17 +65,14 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); - // Then we can use it! - let socket = DnsSocket::new(stack); - let host = "example.com"; info!("querying host {:?}...", host); - match socket.query(host, DnsQueryType::A).await { + match stack.dns_query(host, DnsQueryType::A).await { Ok(r) => { info!("query response: {:?}", r); } From 4c4e923e057cc376ae3ebc2c2f7a01ed4723d308 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 22:47:17 +0100 Subject: [PATCH 366/374] nrf/qspi: do not panic when canceling futures. --- embassy-nrf/src/qspi.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 07a97001..d434327f 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::ptr; use core::task::Poll; -use embassy_hal_common::drop::DropBomb; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{self, Pin as GpioPin}; @@ -190,7 +190,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { // Enable it r.enable.write(|w| w.enable().enabled()); - let mut res = Self { + let res = Self { dpm_enabled: config.deep_power_down.is_some(), irq, }; @@ -200,7 +200,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { r.tasks_activate.write(|w| w.tasks_activate().bit(true)); - res.blocking_wait_ready(); + Self::blocking_wait_ready(); res } @@ -217,7 +217,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); let len = core::cmp::max(req.len(), resp.len()) as u8; self.custom_instruction_start(opcode, req, len)?; @@ -226,7 +226,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { self.custom_instruction_finish(resp)?; - bomb.defuse(); + ondrop.defuse(); Ok(()) } @@ -236,7 +236,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { let len = core::cmp::max(req.len(), resp.len()) as u8; self.custom_instruction_start(opcode, req, len)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); self.custom_instruction_finish(resp)?; @@ -312,7 +312,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { .await } - fn blocking_wait_ready(&mut self) { + fn blocking_wait_ready() { loop { let r = T::regs(); if r.events_ready.read().bits() != 0 { @@ -382,36 +382,36 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { /// Read data from the flash memory. pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_read(address, data)?; self.wait_ready().await; - bomb.defuse(); + ondrop.defuse(); Ok(()) } /// Write data to the flash memory. pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_write(address, data)?; self.wait_ready().await; - bomb.defuse(); + ondrop.defuse(); Ok(()) } /// Erase a sector on the flash memory. pub async fn erase(&mut self, address: usize) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_erase(address)?; self.wait_ready().await; - bomb.defuse(); + ondrop.defuse(); Ok(()) } @@ -419,21 +419,21 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { /// Read data from the flash memory, blocking version. pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { self.start_read(address, data)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); Ok(()) } /// Write data to the flash memory, blocking version. pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { self.start_write(address, data)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); Ok(()) } /// Erase a sector on the flash memory, blocking version. pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { self.start_erase(address)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); Ok(()) } } From 76642b3a3cddaa226dcd741d5f9f8e9d01c2f3ac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 23:35:44 +0100 Subject: [PATCH 367/374] fix h7 examples --- examples/nrf52840/src/bin/usb_ethernet.rs | 23 +++++++++++++++++++++++ examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 97978089..430468ad 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -46,8 +46,31 @@ async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } +#[inline(never)] +pub fn test_function() -> (usize, u32, [u32; 2]) { + let mut array = [3; 2]; + + let mut index = 0; + let mut result = 0; + + for x in [1, 2] { + if x == 1 { + array[1] = 99; + } else { + index = if x == 2 { 1 } else { 0 }; + + // grabs value from array[0], not array[1] + result = array[index]; + } + } + + (index, result, array) +} + #[embassy_executor::main] async fn main(spawner: Spawner) { + info!("{:?}", test_function()); + let p = embassy_nrf::init(Default::default()); let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index bcf97641..a0413478 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -21,7 +21,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = { version = "=0.2.0-alpha.0" } -embedded-nal-async = "0.3.0" +embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } From d21643c060376fff0f0f5b8c5bc4eed4df4bc560 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sun, 12 Feb 2023 11:31:22 +0100 Subject: [PATCH 368/374] fix "prescaler none" which incorrectly set "prescaler divided by 3" --- embassy-stm32/src/rcc/wl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 34767491..82b0d04e 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -158,7 +158,7 @@ impl Into for APBPrescaler { impl Into for AHBPrescaler { fn into(self) -> u8 { match self { - AHBPrescaler::NotDivided => 1, + AHBPrescaler::NotDivided => 0x0, AHBPrescaler::Div2 => 0x08, AHBPrescaler::Div3 => 0x01, AHBPrescaler::Div4 => 0x09, From 80b7c3cf69141c76ba2ae099225c2580cd430086 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 01:30:53 +0100 Subject: [PATCH 369/374] Fix doc build. --- .github/workflows/doc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 8a341b8f..827f8817 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: crates: - - stm32 + #- stm32 # runs out of disk space... - rest # This will ensure at most one doc build job is running at a time @@ -46,7 +46,7 @@ jobs: - name: Install docserver run: | - wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder" + wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.4/builder" chmod +x /usr/local/bin/builder - name: build-stm32 From 1e36c91bf884ed16b898bd5a79e32ee0e62471ef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 02:22:06 +0100 Subject: [PATCH 370/374] stm32: fix fmc-related build failures on some F4's --- embassy-stm32/src/{fmc/mod.rs => fmc.rs} | 130 +++++++++++++++++++++-- embassy-stm32/src/fmc/pins.rs | 118 -------------------- 2 files changed, 122 insertions(+), 126 deletions(-) rename embassy-stm32/src/{fmc/mod.rs => fmc.rs} (64%) delete mode 100644 embassy-stm32/src/fmc/pins.rs diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc.rs similarity index 64% rename from embassy-stm32/src/fmc/mod.rs rename to embassy-stm32/src/fmc.rs index 5adfa0d4..b9129cb5 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc.rs @@ -6,9 +6,6 @@ use crate::gpio::sealed::AFType; use crate::gpio::{Pull, Speed}; use crate::Peripheral; -mod pins; -pub use pins::*; - pub struct Fmc<'d, T: Instance> { peri: PhantomData<&'d mut T>, } @@ -19,7 +16,7 @@ unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T> where T: Instance, { - const REGISTERS: *const () = crate::pac::FMC.0 as *const _; + const REGISTERS: *const () = T::REGS.0 as *const _; fn enable(&mut self) { ::enable(); @@ -32,7 +29,7 @@ where // This is a "not" because it is expected that all future versions have this bit #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))] unsafe { - T::regs().bcr1().modify(|r| r.set_fmcen(true)) + T::REGS.bcr1().modify(|r| r.set_fmcen(true)) }; } @@ -150,13 +147,130 @@ impl<'d, T: Instance> Fmc<'d, T> { )); } +pub(crate) mod sealed { + pub trait Instance: crate::rcc::sealed::RccPeripheral { + const REGS: crate::pac::fmc::Fmc; + } +} + +pub trait Instance: sealed::Instance + 'static {} + foreach_peripheral!( (fmc, $inst:ident) => { impl crate::fmc::sealed::Instance for crate::peripherals::$inst { - fn regs() -> stm32_metapac::fmc::Fmc { - crate::pac::$inst - } + const REGS: crate::pac::fmc::Fmc = crate::pac::$inst; } impl crate::fmc::Instance for crate::peripherals::$inst {} }; ); + +pin_trait!(SDNWEPin, Instance); +pin_trait!(SDNCASPin, Instance); +pin_trait!(SDNRASPin, Instance); + +pin_trait!(SDNE0Pin, Instance); +pin_trait!(SDNE1Pin, Instance); + +pin_trait!(SDCKE0Pin, Instance); +pin_trait!(SDCKE1Pin, Instance); + +pin_trait!(SDCLKPin, Instance); + +pin_trait!(NBL0Pin, Instance); +pin_trait!(NBL1Pin, Instance); +pin_trait!(NBL2Pin, Instance); +pin_trait!(NBL3Pin, Instance); + +pin_trait!(INTPin, Instance); +pin_trait!(NLPin, Instance); +pin_trait!(NWaitPin, Instance); + +pin_trait!(NE1Pin, Instance); +pin_trait!(NE2Pin, Instance); +pin_trait!(NE3Pin, Instance); +pin_trait!(NE4Pin, Instance); + +pin_trait!(NCEPin, Instance); +pin_trait!(NOEPin, Instance); +pin_trait!(NWEPin, Instance); +pin_trait!(ClkPin, Instance); + +pin_trait!(BA0Pin, Instance); +pin_trait!(BA1Pin, Instance); + +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(D4Pin, Instance); +pin_trait!(D5Pin, Instance); +pin_trait!(D6Pin, Instance); +pin_trait!(D7Pin, Instance); +pin_trait!(D8Pin, Instance); +pin_trait!(D9Pin, Instance); +pin_trait!(D10Pin, Instance); +pin_trait!(D11Pin, Instance); +pin_trait!(D12Pin, Instance); +pin_trait!(D13Pin, Instance); +pin_trait!(D14Pin, Instance); +pin_trait!(D15Pin, Instance); +pin_trait!(D16Pin, Instance); +pin_trait!(D17Pin, Instance); +pin_trait!(D18Pin, Instance); +pin_trait!(D19Pin, Instance); +pin_trait!(D20Pin, Instance); +pin_trait!(D21Pin, Instance); +pin_trait!(D22Pin, Instance); +pin_trait!(D23Pin, Instance); +pin_trait!(D24Pin, Instance); +pin_trait!(D25Pin, Instance); +pin_trait!(D26Pin, Instance); +pin_trait!(D27Pin, Instance); +pin_trait!(D28Pin, Instance); +pin_trait!(D29Pin, Instance); +pin_trait!(D30Pin, Instance); +pin_trait!(D31Pin, Instance); + +pin_trait!(DA0Pin, Instance); +pin_trait!(DA1Pin, Instance); +pin_trait!(DA2Pin, Instance); +pin_trait!(DA3Pin, Instance); +pin_trait!(DA4Pin, Instance); +pin_trait!(DA5Pin, Instance); +pin_trait!(DA6Pin, Instance); +pin_trait!(DA7Pin, Instance); +pin_trait!(DA8Pin, Instance); +pin_trait!(DA9Pin, Instance); +pin_trait!(DA10Pin, Instance); +pin_trait!(DA11Pin, Instance); +pin_trait!(DA12Pin, Instance); +pin_trait!(DA13Pin, Instance); +pin_trait!(DA14Pin, Instance); +pin_trait!(DA15Pin, Instance); + +pin_trait!(A0Pin, Instance); +pin_trait!(A1Pin, Instance); +pin_trait!(A2Pin, Instance); +pin_trait!(A3Pin, Instance); +pin_trait!(A4Pin, Instance); +pin_trait!(A5Pin, Instance); +pin_trait!(A6Pin, Instance); +pin_trait!(A7Pin, Instance); +pin_trait!(A8Pin, Instance); +pin_trait!(A9Pin, Instance); +pin_trait!(A10Pin, Instance); +pin_trait!(A11Pin, Instance); +pin_trait!(A12Pin, Instance); +pin_trait!(A13Pin, Instance); +pin_trait!(A14Pin, Instance); +pin_trait!(A15Pin, Instance); +pin_trait!(A16Pin, Instance); +pin_trait!(A17Pin, Instance); +pin_trait!(A18Pin, Instance); +pin_trait!(A19Pin, Instance); +pin_trait!(A20Pin, Instance); +pin_trait!(A21Pin, Instance); +pin_trait!(A22Pin, Instance); +pin_trait!(A23Pin, Instance); +pin_trait!(A24Pin, Instance); +pin_trait!(A25Pin, Instance); diff --git a/embassy-stm32/src/fmc/pins.rs b/embassy-stm32/src/fmc/pins.rs deleted file mode 100644 index 5062e52a..00000000 --- a/embassy-stm32/src/fmc/pins.rs +++ /dev/null @@ -1,118 +0,0 @@ -pub(crate) mod sealed { - pub trait Instance: crate::rcc::sealed::RccPeripheral { - fn regs() -> crate::pac::fmc::Fmc; - } -} - -pub trait Instance: sealed::Instance + 'static {} - -pin_trait!(SDNWEPin, Instance); -pin_trait!(SDNCASPin, Instance); -pin_trait!(SDNRASPin, Instance); - -pin_trait!(SDNE0Pin, Instance); -pin_trait!(SDNE1Pin, Instance); - -pin_trait!(SDCKE0Pin, Instance); -pin_trait!(SDCKE1Pin, Instance); - -pin_trait!(SDCLKPin, Instance); - -pin_trait!(NBL0Pin, Instance); -pin_trait!(NBL1Pin, Instance); -pin_trait!(NBL2Pin, Instance); -pin_trait!(NBL3Pin, Instance); - -pin_trait!(INTPin, Instance); -pin_trait!(NLPin, Instance); -pin_trait!(NWaitPin, Instance); - -pin_trait!(NE1Pin, Instance); -pin_trait!(NE2Pin, Instance); -pin_trait!(NE3Pin, Instance); -pin_trait!(NE4Pin, Instance); - -pin_trait!(NCEPin, Instance); -pin_trait!(NOEPin, Instance); -pin_trait!(NWEPin, Instance); -pin_trait!(ClkPin, Instance); - -pin_trait!(BA0Pin, Instance); -pin_trait!(BA1Pin, Instance); - -pin_trait!(D0Pin, Instance); -pin_trait!(D1Pin, Instance); -pin_trait!(D2Pin, Instance); -pin_trait!(D3Pin, Instance); -pin_trait!(D4Pin, Instance); -pin_trait!(D5Pin, Instance); -pin_trait!(D6Pin, Instance); -pin_trait!(D7Pin, Instance); -pin_trait!(D8Pin, Instance); -pin_trait!(D9Pin, Instance); -pin_trait!(D10Pin, Instance); -pin_trait!(D11Pin, Instance); -pin_trait!(D12Pin, Instance); -pin_trait!(D13Pin, Instance); -pin_trait!(D14Pin, Instance); -pin_trait!(D15Pin, Instance); -pin_trait!(D16Pin, Instance); -pin_trait!(D17Pin, Instance); -pin_trait!(D18Pin, Instance); -pin_trait!(D19Pin, Instance); -pin_trait!(D20Pin, Instance); -pin_trait!(D21Pin, Instance); -pin_trait!(D22Pin, Instance); -pin_trait!(D23Pin, Instance); -pin_trait!(D24Pin, Instance); -pin_trait!(D25Pin, Instance); -pin_trait!(D26Pin, Instance); -pin_trait!(D27Pin, Instance); -pin_trait!(D28Pin, Instance); -pin_trait!(D29Pin, Instance); -pin_trait!(D30Pin, Instance); -pin_trait!(D31Pin, Instance); - -pin_trait!(DA0Pin, Instance); -pin_trait!(DA1Pin, Instance); -pin_trait!(DA2Pin, Instance); -pin_trait!(DA3Pin, Instance); -pin_trait!(DA4Pin, Instance); -pin_trait!(DA5Pin, Instance); -pin_trait!(DA6Pin, Instance); -pin_trait!(DA7Pin, Instance); -pin_trait!(DA8Pin, Instance); -pin_trait!(DA9Pin, Instance); -pin_trait!(DA10Pin, Instance); -pin_trait!(DA11Pin, Instance); -pin_trait!(DA12Pin, Instance); -pin_trait!(DA13Pin, Instance); -pin_trait!(DA14Pin, Instance); -pin_trait!(DA15Pin, Instance); - -pin_trait!(A0Pin, Instance); -pin_trait!(A1Pin, Instance); -pin_trait!(A2Pin, Instance); -pin_trait!(A3Pin, Instance); -pin_trait!(A4Pin, Instance); -pin_trait!(A5Pin, Instance); -pin_trait!(A6Pin, Instance); -pin_trait!(A7Pin, Instance); -pin_trait!(A8Pin, Instance); -pin_trait!(A9Pin, Instance); -pin_trait!(A10Pin, Instance); -pin_trait!(A11Pin, Instance); -pin_trait!(A12Pin, Instance); -pin_trait!(A13Pin, Instance); -pin_trait!(A14Pin, Instance); -pin_trait!(A15Pin, Instance); -pin_trait!(A16Pin, Instance); -pin_trait!(A17Pin, Instance); -pin_trait!(A18Pin, Instance); -pin_trait!(A19Pin, Instance); -pin_trait!(A20Pin, Instance); -pin_trait!(A21Pin, Instance); -pin_trait!(A22Pin, Instance); -pin_trait!(A23Pin, Instance); -pin_trait!(A24Pin, Instance); -pin_trait!(A25Pin, Instance); From 951f2089156288288b8402b2b4b1d3feacdfe975 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 02:37:53 +0100 Subject: [PATCH 371/374] Add more crates to docs. --- .github/workflows/doc.yml | 2 ++ embassy-net-driver-channel/Cargo.toml | 6 ++++++ embassy-net-driver/Cargo.toml | 2 +- embassy-usb-logger/Cargo.toml | 5 +++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 827f8817..4bc73fdc 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -70,12 +70,14 @@ jobs: builder ./embassy-lora crates/embassy-lora/git.zup builder ./embassy-net crates/embassy-net/git.zup builder ./embassy-net-driver crates/embassy-net-driver/git.zup + builder ./embassy-net-driver-channel crates/embassy-net-driver-channel/git.zup builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup builder ./embassy-time crates/embassy-time/git.zup builder ./embassy-usb crates/embassy-usb/git.zup builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup + builder ./embassy-usb-logger crates/embassy-usb-logger/git.zup - name: upload run: | diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index 700a4e8a..6cb6c356 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -3,6 +3,12 @@ name = "embassy-net-driver-channel" version = "0.1.0" edition = "2021" +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver-channel/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index 7ab9d119..ff6f2935 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/" +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/" features = ["defmt"] target = "thumbv7em-none-eabi" diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 2f466592..18d70b0c 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -3,6 +3,11 @@ name = "embassy-usb-logger" version = "0.1.0" edition = "2021" +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-logger-v$VERSION/embassy-usb/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-logger/src/" +target = "thumbv7em-none-eabi" + [dependencies] embassy-usb = { version = "0.1.0", path = "../embassy-usb" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } From 4e15043fc2047751c5580e500a6cd7a0b316f8f5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 02:40:29 +0100 Subject: [PATCH 372/374] add stm32f413vh to CI --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index b325e376..4199f91d 100755 --- a/ci.sh +++ b/ci.sh @@ -63,6 +63,7 @@ cargo batch \ --- 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,stm32f413vh,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,embedded-sdmmc \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ From 363054de983f15a9344ca58a05e41c425661d3db Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 03:02:12 +0100 Subject: [PATCH 373/374] stm32: doc all chips. --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 10e5e48c..82b1ba3a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -17,7 +17,7 @@ flavors = [ { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32f42.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, From 41a563aae3e474955892b27487e185f5f486f525 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 03:11:16 +0100 Subject: [PATCH 374/374] net: document all features --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2d8c5c7b..ca34262d 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] target = "thumbv7em-none-eabi" [features]