From 967fa1b2a2b551dfac9925e46817c97c7ec711f7 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Mon, 17 Jun 2024 20:56:40 -0600 Subject: stm32 i2c slave --- embassy-stm32/src/i2c/config.rs | 149 ++++++++++++++ embassy-stm32/src/i2c/mod.rs | 161 ++++++++++++--- embassy-stm32/src/i2c/v2.rs | 442 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 681 insertions(+), 71 deletions(-) create mode 100644 embassy-stm32/src/i2c/config.rs diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs new file mode 100644 index 000000000..eba14f6e0 --- /dev/null +++ b/embassy-stm32/src/i2c/config.rs @@ -0,0 +1,149 @@ +use stm32_metapac::i2c::vals::Oamsk; + +use crate::gpio::Pull; + +#[repr(u8)] +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AddrMask { + NOMASK, + MASK1, + MASK2, + MASK3, + MASK4, + MASK5, + MASK6, + MASK7, +} +impl From for Oamsk { + fn from(value: AddrMask) -> Self { + match value { + AddrMask::NOMASK => Oamsk::NOMASK, + AddrMask::MASK1 => Oamsk::MASK1, + AddrMask::MASK2 => Oamsk::MASK2, + AddrMask::MASK3 => Oamsk::MASK3, + AddrMask::MASK4 => Oamsk::MASK4, + AddrMask::MASK5 => Oamsk::MASK5, + AddrMask::MASK6 => Oamsk::MASK6, + AddrMask::MASK7 => Oamsk::MASK7, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Address { + SevenBit(u8), + TenBit(u16), +} +impl From for Address { + fn from(value: u8) -> Self { + Address::SevenBit(value) + } +} +impl From for Address { + fn from(value: u16) -> Self { + assert!(value < 0x400, "Ten bit address must be less than 0x400"); + Address::TenBit(value) + } +} +impl Address { + pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode { + match self { + Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7, + Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10, + } + } + pub fn addr(&self) -> u16 { + match self { + Address::SevenBit(addr) => *addr as u16, + Address::TenBit(addr) => *addr, + } + } +} + +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OA2 { + pub addr: u8, + pub mask: AddrMask, +} + +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum OwnAddresses { + OA1(Address), + OA2(OA2), + Both { oa1: Address, oa2: OA2 }, +} + +/// Slave Configuration +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SlaveAddrConfig { + /// Target Address(es) + pub addr: OwnAddresses, + /// Control if the peripheral should respond to the general call address + pub general_call: bool, +} +impl SlaveAddrConfig { + pub fn new_oa1(addr: Address, general_call: bool) -> Self { + Self { + addr: OwnAddresses::OA1(addr), + general_call, + } + } + + pub fn basic(addr: Address) -> Self { + Self { + addr: OwnAddresses::OA1(addr), + general_call: false, + } + } +} + +/// I2C config +#[non_exhaustive] +#[derive(Copy, Clone)] +pub struct Config { + /// Enable internal pullup on SDA. + /// + /// Using external pullup resistors is recommended for I2C. If you do + /// have external pullups you should not enable this. + pub sda_pullup: bool, + /// Enable internal pullup on SCL. + /// + /// Using external pullup resistors is recommended for I2C. If you do + /// have external pullups you should not enable this. + pub scl_pullup: bool, + /// Timeout. + #[cfg(feature = "time")] + pub timeout: embassy_time::Duration, +} + +impl Default for Config { + fn default() -> Self { + Self { + sda_pullup: false, + scl_pullup: false, + #[cfg(feature = "time")] + timeout: embassy_time::Duration::from_millis(1000), + } + } +} + +impl Config { + pub(super) fn scl_pull_mode(&self) -> Pull { + match self.scl_pullup { + true => Pull::Up, + false => Pull::Down, + } + } + + pub(super) fn sda_pull_mode(&self) -> Pull { + match self.sda_pullup { + true => Pull::Up, + false => Pull::Down, + } + } +} diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 739e960b9..0457595d2 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -5,14 +5,18 @@ #[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")] mod _version; +mod config; + use core::future::Future; use core::iter; use core::marker::PhantomData; +pub use config::*; use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; +use mode::{Master, MasterMode, MultiMaster}; use crate::dma::ChannelAndRequest; #[cfg(gpio_v2)] @@ -108,8 +112,56 @@ impl Config { } } +/// I2C modes +pub mod mode { + trait SealedMode {} + + /// Trait for I2C master operations. + #[allow(private_bounds)] + pub trait MasterMode: SealedMode {} + + /// Mode allowing for I2C master operations. + pub struct Master; + /// Mode allowing for I2C master and slave operations. + pub struct MultiMaster; + + impl SealedMode for Master {} + impl MasterMode for Master {} + + impl SealedMode for MultiMaster {} + impl MasterMode for MultiMaster {} +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The command kind to the slave from the master +pub enum CommandKind { + /// Write to the slave + SlaveReceive, + /// Read from the slave + SlaveSend, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The command kind to the slave from the master and the address that the slave matched +pub struct Command { + pub kind: CommandKind, + pub address: Address, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The status of the slave send operation +pub enum SendStatus { + /// The slave send operation is done, all bytes have been sent and the master is not requesting more + Done, + /// The slave send operation is done, but there are leftover bytes that the master did not read + LeftoverBytes(usize), +} + /// I2C driver. -pub struct I2c<'d, M: Mode> { +pub struct I2c<'d, M: Mode, IM: MasterMode> { info: &'static Info, state: &'static State, kernel_clock: Hertz, @@ -120,9 +172,10 @@ pub struct I2c<'d, M: Mode> { #[cfg(feature = "time")] timeout: Duration, _phantom: PhantomData, + _phantom2: PhantomData, } -impl<'d> I2c<'d, Async> { +impl<'d> I2c<'d, Async, Master> { /// Create a new I2C driver. pub fn new( peri: impl Peripheral

+ 'd, @@ -148,7 +201,7 @@ impl<'d> I2c<'d, Async> { } } -impl<'d> I2c<'d, Blocking> { +impl<'d> I2c<'d, Blocking, Master> { /// Create a new blocking I2C driver. pub fn new_blocking( peri: impl Peripheral

+ 'd, @@ -169,7 +222,7 @@ impl<'d> I2c<'d, Blocking> { } } -impl<'d, M: Mode> I2c<'d, M> { +impl<'d, M: Mode> I2c<'d, M, Master> { /// Create a new I2C driver. fn new_inner( _peri: impl Peripheral

+ 'd, @@ -194,8 +247,10 @@ impl<'d, M: Mode> I2c<'d, M> { #[cfg(feature = "time")] timeout: config.timeout, _phantom: PhantomData, + _phantom2: PhantomData, }; this.enable_and_init(freq, config); + this } @@ -203,7 +258,9 @@ impl<'d, M: Mode> I2c<'d, M> { self.info.rcc.enable_and_reset(); self.init(freq, config); } +} +impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { fn timeout(&self) -> Timeout { Timeout { #[cfg(feature = "time")] @@ -211,8 +268,28 @@ impl<'d, M: Mode> I2c<'d, M> { } } } +impl<'d, M: Mode> I2c<'d, M, Master> { + /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) + pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { + let mut slave = I2c { + info: self.info, + state: self.state, + kernel_clock: self.kernel_clock, + scl: self.scl.take(), + sda: self.sda.take(), + tx_dma: self.tx_dma.take(), + rx_dma: self.rx_dma.take(), + #[cfg(feature = "time")] + timeout: self.timeout, + _phantom: PhantomData, + _phantom2: PhantomData, + }; + slave.init_slave(slave_addr_config); + slave + } +} -impl<'d, M: Mode> Drop for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> Drop for I2c<'d, M, IM> { fn drop(&mut self) { self.scl.as_ref().map(|x| x.set_as_disconnected()); self.sda.as_ref().map(|x| x.set_as_disconnected()); @@ -329,27 +406,39 @@ foreach_peripheral!( }; ); -impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> + embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> +where + A: Into

, +{ type Error = Error; - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address.into(), buffer) } } -impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> + embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> +where + A: Into
, +{ type Error = Error; - fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, write) + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address.into(), write) } } -impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> + embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> +where + A: Into
, +{ type Error = Error; - fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, write, read) + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address.into(), write, read) } } @@ -369,51 +458,57 @@ impl embedded_hal_1::i2c::Error for Error { } } -impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> { type Error = Error; } -impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { - fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, read) +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_1::i2c::AddressMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> +where + Address: From, +{ + fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address.into(), read) } - fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, write) + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address.into(), write) } - fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, write, read) + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address.into(), write, read) } fn transaction( &mut self, - address: u8, + address: A, operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { - self.blocking_transaction(address, operations) + self.blocking_transaction(address.into(), operations) } } -impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> { - async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.read(address, read).await +impl<'d, IM: MasterMode, A: embedded_hal_async::i2c::AddressMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> +where + Address: From, +{ + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + self.read(address.into(), read).await } - async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.write(address, write).await + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.write(address.into(), write).await } - async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.write_read(address, write, read).await + async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(address.into(), write, read).await } async fn transaction( &mut self, - address: u8, + address: A, operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { - self.transaction(address, operations).await + self.transaction(address.into(), operations).await } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 8c8df79dd..c2560d819 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -2,9 +2,12 @@ use core::cmp; use core::future::poll_fn; use core::task::Poll; +use config::{Address, OwnAddresses, OA2}; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; +use mode::Master; +use stm32_metapac::i2c::vals::Addmode; use super::*; use crate::pac::i2c; @@ -13,17 +16,21 @@ pub(crate) unsafe fn on_interrupt() { let regs = T::info().regs; let isr = regs.isr().read(); - if isr.tcr() || isr.tc() { + if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() { T::state().waker.wake(); } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt + critical_section::with(|_| { - regs.cr1().modify(|w| w.set_tcie(false)); + regs.cr1().modify(|w| { + w.set_addrie(false); + // The flag can only be cleared by writting to nbytes, we won't do that here, so disable + // the interrupt + w.set_tcie(false); + }); }); } -impl<'d, M: Mode> I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); @@ -51,7 +58,7 @@ impl<'d, M: Mode> I2c<'d, M> { fn master_read( info: &'static Info, - address: u8, + address: Address, length: usize, stop: Stop, reload: bool, @@ -80,8 +87,8 @@ impl<'d, M: Mode> I2c<'d, M> { }; info.regs.cr2().modify(|w| { - w.set_sadd((address << 1 | 0) as u16); - w.set_add10(i2c::vals::Addmode::BIT7); + w.set_sadd(address.addr() << 1); + w.set_add10(address.add_mode()); w.set_dir(i2c::vals::Dir::READ); w.set_nbytes(length as u8); w.set_start(true); @@ -94,7 +101,7 @@ impl<'d, M: Mode> I2c<'d, M> { fn master_write( info: &'static Info, - address: u8, + address: Address, length: usize, stop: Stop, reload: bool, @@ -124,8 +131,8 @@ impl<'d, M: Mode> I2c<'d, M> { // START bit can be set even if the bus is BUSY or // I2C is in slave mode. info.regs.cr2().modify(|w| { - w.set_sadd((address << 1 | 0) as u16); - w.set_add10(i2c::vals::Addmode::BIT7); + w.set_sadd(address.addr() << 1); + w.set_add10(address.add_mode()); w.set_dir(i2c::vals::Dir::WRITE); w.set_nbytes(length as u8); w.set_start(true); @@ -136,14 +143,14 @@ impl<'d, M: Mode> I2c<'d, M> { Ok(()) } - fn master_continue(info: &'static Info, length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { + fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { assert!(length < 256 && length > 0); while !info.regs.isr().read().tcr() { timeout.check()?; } - let reload = if reload { + let will_reload = if will_reload { i2c::vals::Reload::NOTCOMPLETED } else { i2c::vals::Reload::COMPLETED @@ -151,7 +158,7 @@ impl<'d, M: Mode> I2c<'d, M> { info.regs.cr2().modify(|w| { w.set_nbytes(length as u8); - w.set_reload(reload); + w.set_reload(will_reload); }); Ok(()) @@ -229,7 +236,13 @@ impl<'d, M: Mode> I2c<'d, M> { } } - fn read_internal(&mut self, address: u8, read: &mut [u8], restart: bool, timeout: Timeout) -> Result<(), Error> { + fn read_internal( + &mut self, + address: Address, + read: &mut [u8], + restart: bool, + timeout: Timeout, + ) -> Result<(), Error> { let completed_chunks = read.len() / 255; let total_chunks = if completed_chunks * 255 == read.len() { completed_chunks @@ -250,7 +263,7 @@ impl<'d, M: Mode> I2c<'d, M> { for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { @@ -263,7 +276,13 @@ impl<'d, M: Mode> I2c<'d, M> { Ok(()) } - fn write_internal(&mut self, address: u8, write: &[u8], send_stop: bool, timeout: Timeout) -> Result<(), Error> { + fn write_internal( + &mut self, + address: Address, + write: &[u8], + send_stop: bool, + timeout: Timeout, + ) -> Result<(), Error> { let completed_chunks = write.len() / 255; let total_chunks = if completed_chunks * 255 == write.len() { completed_chunks @@ -291,7 +310,7 @@ impl<'d, M: Mode> I2c<'d, M> { for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { @@ -320,18 +339,18 @@ impl<'d, M: Mode> I2c<'d, M> { // Blocking public API /// Blocking read. - pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, address: Address, read: &mut [u8]) -> Result<(), Error> { self.read_internal(address, read, false, self.timeout()) // Automatic Stop } /// Blocking write. - pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, address: Address, write: &[u8]) -> Result<(), Error> { self.write_internal(address, write, true, self.timeout()) } /// Blocking write, restart, read. - pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: Address, write: &[u8], read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); self.write_internal(address, write, false, timeout)?; self.read_internal(address, read, true, timeout) @@ -343,7 +362,7 @@ impl<'d, M: Mode> I2c<'d, M> { /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + pub fn blocking_transaction(&mut self, addr: Address, operations: &mut [Operation<'_>]) -> Result<(), Error> { let _ = addr; let _ = operations; todo!() @@ -352,7 +371,7 @@ impl<'d, M: Mode> I2c<'d, M> { /// Blocking write multiple buffers. /// /// The buffers are concatenated in a single write transaction. - pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + pub fn blocking_write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { if write.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -385,7 +404,7 @@ impl<'d, M: Mode> I2c<'d, M> { let last_chunk_idx = total_chunks.saturating_sub(1); if idx != 0 { - if let Err(err) = Self::master_continue( + if let Err(err) = Self::reload( self.info, slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), @@ -398,7 +417,7 @@ impl<'d, M: Mode> I2c<'d, M> { for (number, chunk) in slice.chunks(255).enumerate() { if number != 0 { - if let Err(err) = Self::master_continue( + if let Err(err) = Self::reload( self.info, chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), @@ -431,10 +450,10 @@ impl<'d, M: Mode> I2c<'d, M> { } } -impl<'d> I2c<'d, Async> { +impl<'d, IM: MasterMode> I2c<'d, Async, IM> { async fn write_dma_internal( &mut self, - address: u8, + address: Address, write: &[u8], first_slice: bool, last_slice: bool, @@ -482,7 +501,7 @@ impl<'d> I2c<'d, Async> { timeout, )?; } else { - Self::master_continue(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; + Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -493,7 +512,7 @@ impl<'d> I2c<'d, Async> { } else { let last_piece = (remaining_len <= 255) && last_slice; - if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -519,7 +538,7 @@ impl<'d> I2c<'d, Async> { async fn read_dma_internal( &mut self, - address: u8, + address: Address, buffer: &mut [u8], restart: bool, timeout: Timeout, @@ -569,7 +588,7 @@ impl<'d> I2c<'d, Async> { } else { let last_piece = remaining_len <= 255; - if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -590,12 +609,11 @@ impl<'d> I2c<'d, Async> { Ok(()) } - // ========================= // Async public API /// Write. - pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + pub async fn write(&mut self, address: Address, write: &[u8]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { self.write_internal(address, write, true, timeout) @@ -609,7 +627,7 @@ impl<'d> I2c<'d, Async> { /// Write multiple buffers. /// /// The buffers are concatenated in a single write transaction. - pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { @@ -632,7 +650,7 @@ impl<'d> I2c<'d, Async> { } /// Read. - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + pub async fn read(&mut self, address: Address, buffer: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); if buffer.is_empty() { @@ -644,7 +662,7 @@ impl<'d> I2c<'d, Async> { } /// Write, restart, read. - pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + pub async fn write_read(&mut self, address: Address, write: &[u8], read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { @@ -669,13 +687,343 @@ impl<'d> I2c<'d, Async> { /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + pub async fn transaction(&mut self, addr: Address, operations: &mut [Operation<'_>]) -> Result<(), Error> { let _ = addr; let _ = operations; todo!() } } +impl<'d, M: Mode> I2c<'d, M, MultiMaster> { + pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { + self.info.regs.cr1().modify(|reg| { + reg.set_pe(false); + }); + + self.info.regs.cr1().modify(|reg| { + reg.set_nostretch(false); + reg.set_gcen(config.general_call); + reg.set_sbc(true); + reg.set_pe(true); + }); + + self.reconfigure_addresses(config.addr); + } + + /// Configure the slave address. + pub fn reconfigure_addresses(&mut self, addresses: OwnAddresses) { + match addresses { + OwnAddresses::OA1(oa1) => self.configure_oa1(oa1), + OwnAddresses::OA2(oa2) => self.configure_oa2(oa2), + OwnAddresses::Both { oa1, oa2 } => { + self.configure_oa1(oa1); + self.configure_oa2(oa2); + } + } + } + + fn configure_oa1(&mut self, oa1: Address) { + match oa1 { + Address::SevenBit(addr) => self.info.regs.oar1().write(|reg| { + reg.set_oa1en(false); + reg.set_oa1((addr << 1) as u16); + reg.set_oa1mode(Addmode::BIT7); + reg.set_oa1en(true); + }), + Address::TenBit(addr) => self.info.regs.oar1().write(|reg| { + reg.set_oa1en(false); + reg.set_oa1(addr); + reg.set_oa1mode(Addmode::BIT10); + reg.set_oa1en(true); + }), + } + } + + fn configure_oa2(&mut self, oa2: OA2) { + self.info.regs.oar2().write(|reg| { + reg.set_oa2en(false); + reg.set_oa2msk(oa2.mask.into()); + reg.set_oa2(oa2.addr << 1); + reg.set_oa2en(true); + }); + } + + fn determine_matched_address(&self) -> Result { + let matched = self.info.regs.isr().read().addcode(); + + if matched >> 3 == 0b11110 { + // is 10-bit address and we need to get the other 8 bits from the rxdr + // we do this by doing a blocking read of 1 byte + let mut buffer = [0]; + self.slave_read_internal(&mut buffer, self.timeout())?; + Ok(Address::TenBit((matched as u16) << 6 | buffer[0] as u16)) + } else { + Ok(Address::SevenBit(matched)) + } + } +} + +impl<'d, M: Mode> I2c<'d, M, MultiMaster> { + /// # Safety + /// This function will clear the address flag which will stop the clock stretching. + /// This should only be done after the dma transfer has been set up. + fn slave_start(info: &'static Info, length: usize, reload: bool) { + assert!(length < 256); + + let reload = if reload { + i2c::vals::Reload::NOTCOMPLETED + } else { + i2c::vals::Reload::COMPLETED + }; + + info.regs.cr2().modify(|w| { + w.set_nbytes(length as u8); + w.set_reload(reload); + }); + + // clear the address flag, will stop the clock stretching. + // this should only be done after the dma transfer has been set up. + info.regs.icr().modify(|reg| reg.set_addrcf(true)); + } + + // A blocking read operation + fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<(), Error> { + let completed_chunks = read.len() / 255; + let total_chunks = if completed_chunks * 255 == read.len() { + completed_chunks + } else { + completed_chunks + 1 + }; + let last_chunk_idx = total_chunks.saturating_sub(1); + for (number, chunk) in read.chunks_mut(255).enumerate() { + if number != 0 { + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + } + + for byte in chunk { + // Wait until we have received something + self.wait_rxne(timeout)?; + + *byte = self.info.regs.rxdr().read().rxdata(); + } + } + + Ok(()) + } + + // A blocking write operation + fn slave_write_internal(&mut self, write: &[u8], timeout: Timeout) -> Result<(), Error> { + let completed_chunks = write.len() / 255; + let total_chunks = if completed_chunks * 255 == write.len() { + completed_chunks + } else { + completed_chunks + 1 + }; + let last_chunk_idx = total_chunks.saturating_sub(1); + + for (number, chunk) in write.chunks(255).enumerate() { + if number != 0 { + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + } + + for byte in chunk { + // Wait until we are allowed to send data + // (START has been ACKed or last byte when + // through) + self.wait_txe(timeout)?; + + self.info.regs.txdr().write(|w| w.set_txdata(*byte)); + } + } + Ok(()) + } + + /// Listen for incoming I2C messages. + /// + /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. + pub async fn listen(&mut self) -> Result { + let state = self.state; + self.info.regs.cr1().modify(|reg| { + reg.set_addrie(true); + }); + + poll_fn(|cx| { + state.waker.register(cx.waker()); + let isr = self.info.regs.isr().read(); + if !isr.addr() { + Poll::Pending + } else { + // we do not clear the address flag here as it will be cleared by the dma read/write + // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it + match isr.dir() { + i2c::vals::Dir::WRITE => Poll::Ready(Ok(Command { + kind: CommandKind::SlaveReceive, + address: self.determine_matched_address()?, + })), + i2c::vals::Dir::READ => Poll::Ready(Ok(Command { + kind: CommandKind::SlaveSend, + address: self.determine_matched_address()?, + })), + } + } + }) + .await + } + + /// Respond to a receive command. + pub fn blocking_respond_to_receive(&self, read: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + self.slave_read_internal(read, timeout) + } + + /// Respond to a send command. + pub fn blocking_respond_to_send(&mut self, write: &[u8]) -> Result<(), Error> { + let timeout = self.timeout(); + self.slave_write_internal(write, timeout) + } +} + +impl<'d> I2c<'d, Async, MultiMaster> { + /// Respond to a receive command. + pub async fn respond_to_receive(&mut self, buffer: &mut [u8]) -> Result { + let timeout = self.timeout(); + timeout.with(self.read_dma_internal_slave(buffer, timeout)).await + } + + /// Respond to a send request from an I2C master. + pub async fn respond_to_send(&mut self, write: &[u8]) -> Result { + let timeout = self.timeout(); + timeout.with(self.write_dma_internal_slave(write, timeout)).await + } + + // for data reception in slave mode + async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result { + let total_len = buffer.len(); + let mut remaining_len = total_len; + + let regs = self.info.regs; + + let dma_transfer = unsafe { + regs.cr1().modify(|w| { + w.set_rxdmaen(true); + w.set_stopie(true); + w.set_tcie(true); + }); + let src = regs.rxdr().as_ptr() as *mut u8; + + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) + }; + + let state = self.state; + + let on_drop = OnDrop::new(|| { + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_stopie(false); + w.set_tcie(false); + }) + }); + + let total_received = poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = regs.isr().read(); + + if remaining_len == total_len { + Self::slave_start(self.info, total_len.min(255), total_len > 255); + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + } else if isr.tcr() { + let is_last_slice = remaining_len <= 255; + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { + return Poll::Ready(Err(e)); + } + remaining_len = remaining_len.saturating_sub(255); + regs.cr1().modify(|w| w.set_tcie(true)); + Poll::Pending + } else if isr.stopf() { + regs.icr().write(|reg| reg.set_stopcf(true)); + let poll = Poll::Ready(Ok(remaining_len)); + poll + } else { + Poll::Pending + } + }) + .await?; + + dma_transfer.await; + + drop(on_drop); + + Ok(total_received) + } + + async fn write_dma_internal_slave(&mut self, buffer: &[u8], timeout: Timeout) -> Result { + let total_len = buffer.len(); + let mut remaining_len = total_len; + + let mut dma_transfer = unsafe { + let regs = self.info.regs; + regs.cr1().modify(|w| { + w.set_txdmaen(true); + w.set_stopie(true); + w.set_tcie(true); + }); + let dst = regs.txdr().as_ptr() as *mut u8; + + self.tx_dma.as_mut().unwrap().write(buffer, dst, Default::default()) + }; + + let on_drop = OnDrop::new(|| { + let regs = self.info.regs; + regs.cr1().modify(|w| { + w.set_txdmaen(false); + w.set_stopie(false); + w.set_tcie(false); + }) + }); + + let state = self.state; + + let size = poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = self.info.regs.isr().read(); + + if remaining_len == total_len { + Self::slave_start(self.info, total_len.min(255), total_len > 255); + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + } else if isr.tcr() { + let is_last_slice = remaining_len <= 255; + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { + return Poll::Ready(Err(e)); + } + remaining_len = remaining_len.saturating_sub(255); + self.info.regs.cr1().modify(|w| w.set_tcie(true)); + Poll::Pending + } else if isr.stopf() { + self.info.regs.icr().write(|reg| reg.set_stopcf(true)); + if remaining_len > 0 { + dma_transfer.request_stop(); + Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize))) + } else { + Poll::Ready(Ok(SendStatus::Done)) + } + } else { + Poll::Pending + } + }) + .await?; + + dma_transfer.await; + + drop(on_drop); + + Ok(size) + } +} + /// I2C Stop Configuration /// /// Peripheral options for generating the STOP condition @@ -800,7 +1148,7 @@ impl Timings { } } -impl<'d, M: Mode> SetConfig for I2c<'d, M> { +impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { @@ -816,3 +1164,21 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> { Ok(()) } } + +impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { + type Config = (Hertz, SlaveAddrConfig); + type ConfigError = (); + fn set_config(&mut self, (config, addr_config): &Self::Config) -> Result<(), ()> { + let timings = Timings::new(self.kernel_clock, *config); + self.info.regs.timingr().write(|reg| { + reg.set_presc(timings.prescale); + reg.set_scll(timings.scll); + reg.set_sclh(timings.sclh); + reg.set_sdadel(timings.sdadel); + reg.set_scldel(timings.scldel); + }); + self.init_slave(*addr_config); + + Ok(()) + } +} -- cgit From 49357709ed4f619144746dd04e088d78ad7f355f Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Mon, 17 Jun 2024 21:15:26 -0600 Subject: Move slave constructor to v2 module --- embassy-stm32/src/i2c/mod.rs | 20 -------------------- embassy-stm32/src/i2c/v2.rs | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 0457595d2..a5ff61f3d 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -268,26 +268,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { } } } -impl<'d, M: Mode> I2c<'d, M, Master> { - /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) - pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { - let mut slave = I2c { - info: self.info, - state: self.state, - kernel_clock: self.kernel_clock, - scl: self.scl.take(), - sda: self.sda.take(), - tx_dma: self.tx_dma.take(), - rx_dma: self.rx_dma.take(), - #[cfg(feature = "time")] - timeout: self.timeout, - _phantom: PhantomData, - _phantom2: PhantomData, - }; - slave.init_slave(slave_addr_config); - slave - } -} impl<'d, M: Mode, IM: MasterMode> Drop for I2c<'d, M, IM> { fn drop(&mut self) { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index c2560d819..acf4b3f12 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -694,6 +694,27 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } } +impl<'d, M: Mode> I2c<'d, M, Master> { + /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) + pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { + let mut slave = I2c { + info: self.info, + state: self.state, + kernel_clock: self.kernel_clock, + scl: self.scl.take(), + sda: self.sda.take(), + tx_dma: self.tx_dma.take(), + rx_dma: self.rx_dma.take(), + #[cfg(feature = "time")] + timeout: self.timeout, + _phantom: PhantomData, + _phantom2: PhantomData, + }; + slave.init_slave(slave_addr_config); + slave + } +} + impl<'d, M: Mode> I2c<'d, M, MultiMaster> { pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { self.info.regs.cr1().modify(|reg| { -- cgit From 65c7457c01317a803817cfdfac7b66ab23adf710 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Mon, 17 Jun 2024 21:23:24 -0600 Subject: reference the i2c mode in v1 module --- embassy-stm32/src/i2c/v1.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 28026f83c..a93a5d987 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -11,6 +11,7 @@ use embassy_embedded_hal::SetConfig; use embassy_futures::select::{select, Either}; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; +use mode::Master; use super::*; use crate::mode::Mode as PeriMode; @@ -41,7 +42,7 @@ pub unsafe fn on_interrupt() { }); } -impl<'d, M: PeriMode> I2c<'d, M> { +impl<'d, M: PeriMode> I2c<'d, M, Master> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); @@ -354,7 +355,7 @@ impl<'d, M: PeriMode> I2c<'d, M> { } } -impl<'d> I2c<'d, Async> { +impl<'d> I2c<'d, Async, Master> { async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for @@ -800,7 +801,7 @@ impl Timings { } } -impl<'d, M: PeriMode> SetConfig for I2c<'d, M> { +impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { -- cgit From 70dfbc03b0399ebeb57ba9b8ee177154325db374 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Jun 2024 10:43:34 -0600 Subject: Add more docs --- embassy-stm32/src/i2c/config.rs | 43 +++++++++++++++++++++++++++++++---------- embassy-stm32/src/i2c/mod.rs | 4 +++- embassy-stm32/src/i2c/v2.rs | 2 +- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index eba14f6e0..dac7847e8 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs @@ -5,14 +5,23 @@ use crate::gpio::Pull; #[repr(u8)] #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Bits of the I2C OA2 register to mask out. pub enum AddrMask { + /// No mask NOMASK, + /// OA2\[1\] is masked and don’t care. Only OA2\[7:2\] are compared. MASK1, + /// OA2\[2:1\] are masked and don’t care. Only OA2\[7:3\] are compared. MASK2, + /// OA2\[3:1\] are masked and don’t care. Only OA2\[7:4\] are compared. MASK3, + /// OA2\[4:1\] are masked and don’t care. Only OA2\[7:5\] are compared. MASK4, + /// OA2\[5:1\] are masked and don’t care. Only OA2\[7:6\] are compared. MASK5, + /// OA2\[6:1\] are masked and don’t care. Only OA2\[7:6\] are compared. MASK6, + /// OA2\[7:1\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged MASK7, } impl From for Oamsk { @@ -32,8 +41,13 @@ impl From for Oamsk { #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// An I2C address. Either 7 or 10 bit. pub enum Address { + /// A 7 bit address SevenBit(u8), + /// A 10 bit address. + /// + /// When using an address to configure the Own Address, only the OA1 register can be set to a 10-bit address. TenBit(u16), } impl From for Address { @@ -54,6 +68,9 @@ impl Address { Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10, } } + /// Get the inner address as a u16. + /// + /// For 7 bit addresses, the u8 that was used to store the address is returned as a u16. pub fn addr(&self) -> u16 { match self { Address::SevenBit(addr) => *addr as u16, @@ -64,17 +81,29 @@ impl Address { #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The second Own Address register. pub struct OA2 { + /// The address. pub addr: u8, + /// The bit mask that will affect how the own address 2 register is compared. pub mask: AddrMask, } #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The Own Address(es) of the I2C peripheral. pub enum OwnAddresses { + /// Configuration for only the OA1 register. OA1(Address), + /// Configuration for only the OA2 register. OA2(OA2), - Both { oa1: Address, oa2: OA2 }, + /// Configuration for both the OA1 and OA2 registers. + Both { + /// The [Address] for the OA1 register. + oa1: Address, + /// The [OA2] configuration. + oa2: OA2, + }, } /// Slave Configuration @@ -87,16 +116,10 @@ pub struct SlaveAddrConfig { pub general_call: bool, } impl SlaveAddrConfig { - pub fn new_oa1(addr: Address, general_call: bool) -> Self { - Self { - addr: OwnAddresses::OA1(addr), - general_call, - } - } - - pub fn basic(addr: Address) -> Self { + /// Create a new slave address configuration with only the OA1 register set in 7 bit mode and the general call disabled. + pub fn basic(addr: u8) -> Self { Self { - addr: OwnAddresses::OA1(addr), + addr: OwnAddresses::OA1(Address::SevenBit(addr)), general_call: false, } } diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index a5ff61f3d..1464a6851 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -16,7 +16,7 @@ use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use mode::{Master, MasterMode, MultiMaster}; +use mode::{Master, MasterMode}; use crate::dma::ChannelAndRequest; #[cfg(gpio_v2)] @@ -146,7 +146,9 @@ pub enum CommandKind { #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// The command kind to the slave from the master and the address that the slave matched pub struct Command { + /// The kind of command pub kind: CommandKind, + /// The address that the slave matched pub address: Address, } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index acf4b3f12..86efd0da2 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -6,7 +6,7 @@ use config::{Address, OwnAddresses, OA2}; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; -use mode::Master; +use mode::{Master, MultiMaster}; use stm32_metapac::i2c::vals::Addmode; use super::*; -- cgit From 31f224f43c7b0fa6796b4966b310b0bbe8edd14b Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Jun 2024 10:56:46 -0600 Subject: move embedded hal impl to version modules to allow for 10bit addr on v2 --- embassy-stm32/src/i2c/mod.rs | 86 ------------------------------------------- embassy-stm32/src/i2c/v1.rs | 72 +++++++++++++++++++++++++++++++++++- embassy-stm32/src/i2c/v2.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 87 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 1464a6851..3303b9f4b 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -388,42 +388,6 @@ foreach_peripheral!( }; ); -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> - embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> -where - A: Into
, -{ - type Error = Error; - - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address.into(), buffer) - } -} - -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> - embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> -where - A: Into
, -{ - type Error = Error; - - fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address.into(), write) - } -} - -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> - embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> -where - A: Into
, -{ - type Error = Error; - - fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address.into(), write, read) - } -} - impl embedded_hal_1::i2c::Error for Error { fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { match *self { @@ -444,56 +408,6 @@ impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, type Error = Error; } -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_1::i2c::AddressMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> -where - Address: From, -{ - fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address.into(), read) - } - - fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address.into(), write) - } - - fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address.into(), write, read) - } - - fn transaction( - &mut self, - address: A, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.blocking_transaction(address.into(), operations) - } -} - -impl<'d, IM: MasterMode, A: embedded_hal_async::i2c::AddressMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> -where - Address: From, -{ - async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { - self.read(address.into(), read).await - } - - async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - self.write(address.into(), write).await - } - - async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.write_read(address.into(), write, read).await - } - - async fn transaction( - &mut self, - address: A, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.transaction(address.into(), operations).await - } -} - /// Frame type in I2C transaction. /// /// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index a93a5d987..e98ab0290 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -298,7 +298,7 @@ impl<'d, M: PeriMode> I2c<'d, M, Master> { } /// Blocking read. - pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, addr: Address, read: &mut [u8]) -> Result<(), Error> { self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) } @@ -821,3 +821,73 @@ impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> { Ok(()) } } + +// ======== Embedded HAL impls ======== + +impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } +} + +impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> { + type Error = Error; + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) + } +} + +impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> { + type Error = Error; + + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) + } +} + +impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> { + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) + } + + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.blocking_transaction(address, operations) + } +} + +impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.read(address, read).await + } + + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.write(address, write).await + } + + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(address, write, read).await + } + + async fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.transaction(address, operations).await + } +} diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 86efd0da2..6dd7c4079 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1203,3 +1203,91 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { Ok(()) } } + +// ======== Embedded HAL impls ======== + +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> + embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> +where + A: Into
, +{ + type Error = Error; + + fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address.into(), buffer) + } +} + +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> + embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> +where + A: Into
, +{ + type Error = Error; + + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address.into(), write) + } +} + +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> + embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> +where + A: Into
, +{ + type Error = Error; + + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address.into(), write, read) + } +} + +impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_1::i2c::AddressMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> +where + Address: From, +{ + fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address.into(), read) + } + + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address.into(), write) + } + + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address.into(), write, read) + } + + fn transaction( + &mut self, + address: A, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.blocking_transaction(address.into(), operations) + } +} + +impl<'d, IM: MasterMode, A: embedded_hal_async::i2c::AddressMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> +where + Address: From, +{ + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + self.read(address.into(), read).await + } + + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.write(address.into(), write).await + } + + async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(address.into(), write, read).await + } + + async fn transaction( + &mut self, + address: A, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.transaction(address.into(), operations).await + } +} -- cgit From 98437e39ee3b62289d1d126141d8cfb150e9c03a Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Jun 2024 11:05:12 -0600 Subject: move addr mask impl to be v2 only --- embassy-stm32/src/i2c/config.rs | 16 ---------------- embassy-stm32/src/i2c/v2.rs | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index dac7847e8..6f9c53f1f 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs @@ -1,5 +1,3 @@ -use stm32_metapac::i2c::vals::Oamsk; - use crate::gpio::Pull; #[repr(u8)] @@ -24,20 +22,6 @@ pub enum AddrMask { /// OA2\[7:1\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged MASK7, } -impl From for Oamsk { - fn from(value: AddrMask) -> Self { - match value { - AddrMask::NOMASK => Oamsk::NOMASK, - AddrMask::MASK1 => Oamsk::MASK1, - AddrMask::MASK2 => Oamsk::MASK2, - AddrMask::MASK3 => Oamsk::MASK3, - AddrMask::MASK4 => Oamsk::MASK4, - AddrMask::MASK5 => Oamsk::MASK5, - AddrMask::MASK6 => Oamsk::MASK6, - AddrMask::MASK7 => Oamsk::MASK7, - } - } -} #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 6dd7c4079..b6d714327 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -7,11 +7,26 @@ use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use mode::{Master, MultiMaster}; -use stm32_metapac::i2c::vals::Addmode; +use stm32_metapac::i2c::vals::{Addmode, Oamsk}; use super::*; use crate::pac::i2c; +impl From for Oamsk { + fn from(value: AddrMask) -> Self { + match value { + AddrMask::NOMASK => Oamsk::NOMASK, + AddrMask::MASK1 => Oamsk::MASK1, + AddrMask::MASK2 => Oamsk::MASK2, + AddrMask::MASK3 => Oamsk::MASK3, + AddrMask::MASK4 => Oamsk::MASK4, + AddrMask::MASK5 => Oamsk::MASK5, + AddrMask::MASK6 => Oamsk::MASK6, + AddrMask::MASK7 => Oamsk::MASK7, + } + } +} + pub(crate) unsafe fn on_interrupt() { let regs = T::info().regs; let isr = regs.isr().read(); -- cgit From bec0eac5aba15c38697cfedae948e0a9f3005c56 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Jun 2024 11:13:32 -0600 Subject: Fix v1 mod --- embassy-stm32/src/i2c/v1.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index e98ab0290..30964e3f4 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -42,7 +42,7 @@ pub unsafe fn on_interrupt() { }); } -impl<'d, M: PeriMode> I2c<'d, M, Master> { +impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); @@ -298,7 +298,7 @@ impl<'d, M: PeriMode> I2c<'d, M, Master> { } /// Blocking read. - pub fn blocking_read(&mut self, addr: Address, read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) } @@ -355,7 +355,7 @@ impl<'d, M: PeriMode> I2c<'d, M, Master> { } } -impl<'d> I2c<'d, Async, Master> { +impl<'d, IM: MasterMode> I2c<'d, Async, IM> { async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for -- cgit From 8b50f2a58be0e23ca451d03af7769773e3815b00 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Jun 2024 11:17:15 -0600 Subject: remove dead code when v1 --- embassy-stm32/src/i2c/config.rs | 6 ------ embassy-stm32/src/i2c/v2.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index 6f9c53f1f..2c7676bd9 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs @@ -46,12 +46,6 @@ impl From for Address { } } impl Address { - pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode { - match self { - Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7, - Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10, - } - } /// Get the inner address as a u16. /// /// For 7 bit addresses, the u8 that was used to store the address is returned as a u16. diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index b6d714327..531358efa 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -27,6 +27,15 @@ impl From for Oamsk { } } +impl Address { + pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode { + match self { + Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7, + Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10, + } + } +} + pub(crate) unsafe fn on_interrupt() { let regs = T::info().regs; let isr = regs.isr().read(); -- cgit From c7b4a076cc47b5f824b39cbeae825fc108ddac2a Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Jun 2024 19:05:07 -0600 Subject: add drop guard, clear interrupts, fix len return --- embassy-stm32/src/i2c/mod.rs | 33 ++++++++++++++++++++------------- embassy-stm32/src/i2c/v2.rs | 11 ++++++++--- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 3303b9f4b..82e28b3ff 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -162,19 +162,32 @@ pub enum SendStatus { LeftoverBytes(usize), } +struct I2CDropGuard<'d> { + info: &'static Info, + scl: Option>, + sda: Option>, +} +impl<'d> Drop for I2CDropGuard<'d> { + fn drop(&mut self) { + self.scl.as_ref().map(|x| x.set_as_disconnected()); + self.sda.as_ref().map(|x| x.set_as_disconnected()); + + self.info.rcc.disable(); + } +} + /// I2C driver. pub struct I2c<'d, M: Mode, IM: MasterMode> { info: &'static Info, state: &'static State, kernel_clock: Hertz, - scl: Option>, - sda: Option>, tx_dma: Option>, rx_dma: Option>, #[cfg(feature = "time")] timeout: Duration, _phantom: PhantomData, _phantom2: PhantomData, + drop_guard: I2CDropGuard<'d>, } impl<'d> I2c<'d, Async, Master> { @@ -242,14 +255,17 @@ impl<'d, M: Mode> I2c<'d, M, Master> { info: T::info(), state: T::state(), kernel_clock: T::frequency(), - scl, - sda, tx_dma, rx_dma, #[cfg(feature = "time")] timeout: config.timeout, _phantom: PhantomData, _phantom2: PhantomData, + drop_guard: I2CDropGuard { + info: T::info(), + scl, + sda, + }, }; this.enable_and_init(freq, config); @@ -271,15 +287,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { } } -impl<'d, M: Mode, IM: MasterMode> Drop for I2c<'d, M, IM> { - fn drop(&mut self) { - self.scl.as_ref().map(|x| x.set_as_disconnected()); - self.sda.as_ref().map(|x| x.set_as_disconnected()); - - self.info.rcc.disable() - } -} - #[derive(Copy, Clone)] struct Timeout { #[cfg(feature = "time")] diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 531358efa..a90bdd551 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -47,6 +47,8 @@ pub(crate) unsafe fn on_interrupt() { critical_section::with(|_| { regs.cr1().modify(|w| { w.set_addrie(false); + w.set_stopie(false); + w.set_tcie(false); // The flag can only be cleared by writting to nbytes, we won't do that here, so disable // the interrupt w.set_tcie(false); @@ -725,14 +727,13 @@ impl<'d, M: Mode> I2c<'d, M, Master> { info: self.info, state: self.state, kernel_clock: self.kernel_clock, - scl: self.scl.take(), - sda: self.sda.take(), tx_dma: self.tx_dma.take(), rx_dma: self.rx_dma.take(), #[cfg(feature = "time")] timeout: self.timeout, _phantom: PhantomData, _phantom2: PhantomData, + drop_guard: self.drop_guard, }; slave.init_slave(slave_addr_config); slave @@ -930,6 +931,8 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { impl<'d> I2c<'d, Async, MultiMaster> { /// Respond to a receive command. + /// + /// Returns the total number of bytes received. pub async fn respond_to_receive(&mut self, buffer: &mut [u8]) -> Result { let timeout = self.timeout(); timeout.with(self.read_dma_internal_slave(buffer, timeout)).await @@ -942,6 +945,8 @@ impl<'d> I2c<'d, Async, MultiMaster> { } // for data reception in slave mode + // + // returns the total number of bytes received async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result { let total_len = buffer.len(); let mut remaining_len = total_len; @@ -988,7 +993,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { Poll::Pending } else if isr.stopf() { regs.icr().write(|reg| reg.set_stopcf(true)); - let poll = Poll::Ready(Ok(remaining_len)); + let poll = Poll::Ready(Ok(total_len - remaining_len)); poll } else { Poll::Pending -- cgit From d1f5a4c5c72787cfa7ce9e7c057714e3a272031f Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 25 Jun 2024 14:12:19 -0600 Subject: fix ci --- embassy-stm32/src/i2c/config.rs | 40 +++++++++++++++----- embassy-stm32/src/i2c/mod.rs | 84 +++++------------------------------------ embassy-stm32/src/i2c/v2.rs | 2 +- 3 files changed, 41 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index 2c7676bd9..daae43bcd 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs @@ -1,4 +1,6 @@ +#[cfg(gpio_v2)] use crate::gpio::Pull; +use crate::gpio::{AfType, OutputType, Speed}; #[repr(u8)] #[derive(Copy, Clone)] @@ -111,11 +113,13 @@ pub struct Config { /// /// Using external pullup resistors is recommended for I2C. If you do /// have external pullups you should not enable this. + #[cfg(gpio_v2)] pub sda_pullup: bool, /// Enable internal pullup on SCL. /// /// Using external pullup resistors is recommended for I2C. If you do /// have external pullups you should not enable this. + #[cfg(gpio_v2)] pub scl_pullup: bool, /// Timeout. #[cfg(feature = "time")] @@ -125,7 +129,9 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + #[cfg(gpio_v2)] sda_pullup: false, + #[cfg(gpio_v2)] scl_pullup: false, #[cfg(feature = "time")] timeout: embassy_time::Duration::from_millis(1000), @@ -134,17 +140,31 @@ impl Default for Config { } impl Config { - pub(super) fn scl_pull_mode(&self) -> Pull { - match self.scl_pullup { - true => Pull::Up, - false => Pull::Down, - } + pub(super) fn scl_af(&self) -> AfType { + #[cfg(gpio_v1)] + return AfType::output(OutputType::OpenDrain, Speed::Medium); + #[cfg(gpio_v2)] + return AfType::output_pull( + OutputType::OpenDrain, + Speed::Medium, + match self.scl_pullup { + true => Pull::Up, + false => Pull::Down, + }, + ); } - pub(super) fn sda_pull_mode(&self) -> Pull { - match self.sda_pullup { - true => Pull::Up, - false => Pull::Down, - } + pub(super) fn sda_af(&self) -> AfType { + #[cfg(gpio_v1)] + return AfType::output(OutputType::OpenDrain, Speed::Medium); + #[cfg(gpio_v2)] + return AfType::output_pull( + OutputType::OpenDrain, + Speed::Medium, + match self.sda_pullup { + true => Pull::Up, + false => Pull::Down, + }, + ); } } diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 82e28b3ff..fed2e4714 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -19,9 +19,7 @@ use embassy_time::{Duration, Instant}; use mode::{Master, MasterMode}; use crate::dma::ChannelAndRequest; -#[cfg(gpio_v2)] -use crate::gpio::Pull; -use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; +use crate::gpio::{AnyPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::mode::{Async, Blocking, Mode}; use crate::rcc::{RccInfo, SealedRccPeripheral}; @@ -48,70 +46,6 @@ pub enum Error { ZeroLengthTransfer, } -/// I2C config -#[non_exhaustive] -#[derive(Copy, Clone)] -pub struct Config { - /// Enable internal pullup on SDA. - /// - /// Using external pullup resistors is recommended for I2C. If you do - /// have external pullups you should not enable this. - #[cfg(gpio_v2)] - pub sda_pullup: bool, - /// Enable internal pullup on SCL. - /// - /// Using external pullup resistors is recommended for I2C. If you do - /// have external pullups you should not enable this. - #[cfg(gpio_v2)] - pub scl_pullup: bool, - /// Timeout. - #[cfg(feature = "time")] - pub timeout: embassy_time::Duration, -} - -impl Default for Config { - fn default() -> Self { - Self { - #[cfg(gpio_v2)] - sda_pullup: false, - #[cfg(gpio_v2)] - scl_pullup: false, - #[cfg(feature = "time")] - timeout: embassy_time::Duration::from_millis(1000), - } - } -} - -impl Config { - fn scl_af(&self) -> AfType { - #[cfg(gpio_v1)] - return AfType::output(OutputType::OpenDrain, Speed::Medium); - #[cfg(gpio_v2)] - return AfType::output_pull( - OutputType::OpenDrain, - Speed::Medium, - match self.scl_pullup { - true => Pull::Up, - false => Pull::Down, - }, - ); - } - - fn sda_af(&self) -> AfType { - #[cfg(gpio_v1)] - return AfType::output(OutputType::OpenDrain, Speed::Medium); - #[cfg(gpio_v2)] - return AfType::output_pull( - OutputType::OpenDrain, - Speed::Medium, - match self.sda_pullup { - true => Pull::Up, - false => Pull::Down, - }, - ); - } -} - /// I2C modes pub mod mode { trait SealedMode {} @@ -169,8 +103,12 @@ struct I2CDropGuard<'d> { } impl<'d> Drop for I2CDropGuard<'d> { fn drop(&mut self) { - self.scl.as_ref().map(|x| x.set_as_disconnected()); - self.sda.as_ref().map(|x| x.set_as_disconnected()); + if let Some(x) = self.scl.as_ref() { + x.set_as_disconnected() + } + if let Some(x) = self.sda.as_ref() { + x.set_as_disconnected() + } self.info.rcc.disable(); } @@ -187,7 +125,7 @@ pub struct I2c<'d, M: Mode, IM: MasterMode> { timeout: Duration, _phantom: PhantomData, _phantom2: PhantomData, - drop_guard: I2CDropGuard<'d>, + _drop_guard: I2CDropGuard<'d>, } impl<'d> I2c<'d, Async, Master> { @@ -261,7 +199,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { timeout: config.timeout, _phantom: PhantomData, _phantom2: PhantomData, - drop_guard: I2CDropGuard { + _drop_guard: I2CDropGuard { info: T::info(), scl, sda, @@ -509,9 +447,7 @@ fn operation_frames<'a, 'b: 'a>( let mut next_first_frame = true; Ok(iter::from_fn(move || { - let Some(op) = operations.next() else { - return None; - }; + let op = operations.next()?; // Is `op` first frame of its type? let first_frame = next_first_frame; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index a90bdd551..7ca958fca 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -733,7 +733,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { timeout: self.timeout, _phantom: PhantomData, _phantom2: PhantomData, - drop_guard: self.drop_guard, + _drop_guard: self._drop_guard, }; slave.init_slave(slave_addr_config); slave -- cgit From b4eb4a3d189efe5fe3716bb5dcd6396efcfab5c3 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 13 Aug 2024 09:45:28 -0600 Subject: remove 10 bit support --- embassy-stm32/src/i2c/mod.rs | 68 ++++++++++++++++++++++ embassy-stm32/src/i2c/v1.rs | 70 ----------------------- embassy-stm32/src/i2c/v2.rs | 132 ++++++++----------------------------------- 3 files changed, 90 insertions(+), 180 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index fed2e4714..6f952b8f8 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -333,6 +333,30 @@ foreach_peripheral!( }; ); +impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } +} + +impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> { + type Error = Error; + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) + } +} + +impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> { + type Error = Error; + + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) + } +} + impl embedded_hal_1::i2c::Error for Error { fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { match *self { @@ -353,6 +377,50 @@ impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, type Error = Error; } +impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> { + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) + } + + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.blocking_transaction(address, operations) + } +} + +impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.read(address, read).await + } + + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.write(address, write).await + } + + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(address, write, read).await + } + + async fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.transaction(address, operations).await + } +} + /// Frame type in I2C transaction. /// /// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 30964e3f4..35f13ab46 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -821,73 +821,3 @@ impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> { Ok(()) } } - -// ======== Embedded HAL impls ======== - -impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> { - type Error = Error; - - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) - } -} - -impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> { - type Error = Error; - - fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, write) - } -} - -impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> { - type Error = Error; - - fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, write, read) - } -} - -impl<'d, M: PeriMode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> { - fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, read) - } - - fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, write) - } - - fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, write, read) - } - - fn transaction( - &mut self, - address: u8, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.blocking_transaction(address, operations) - } -} - -impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { - async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.read(address, read).await - } - - async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.write(address, write).await - } - - async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.write_read(address, write, read).await - } - - async fn transaction( - &mut self, - address: u8, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.transaction(address, operations).await - } -} diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7ca958fca..1cc41fb5f 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -365,21 +365,21 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { // Blocking public API /// Blocking read. - pub fn blocking_read(&mut self, address: Address, read: &mut [u8]) -> Result<(), Error> { - self.read_internal(address, read, false, self.timeout()) + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + self.read_internal(address.into(), read, false, self.timeout()) // Automatic Stop } /// Blocking write. - pub fn blocking_write(&mut self, address: Address, write: &[u8]) -> Result<(), Error> { - self.write_internal(address, write, true, self.timeout()) + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + self.write_internal(address.into(), write, true, self.timeout()) } /// Blocking write, restart, read. - pub fn blocking_write_read(&mut self, address: Address, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); - self.write_internal(address, write, false, timeout)?; - self.read_internal(address, read, true, timeout) + self.write_internal(address.into(), write, false, timeout)?; + self.read_internal(address.into(), read, true, timeout) // Automatic Stop } @@ -388,7 +388,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub fn blocking_transaction(&mut self, addr: Address, operations: &mut [Operation<'_>]) -> Result<(), Error> { + pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { let _ = addr; let _ = operations; todo!() @@ -397,7 +397,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { /// Blocking write multiple buffers. /// /// The buffers are concatenated in a single write transaction. - pub fn blocking_write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { + pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { if write.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -409,7 +409,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { if let Err(err) = Self::master_write( self.info, - address, + address.into(), first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), @@ -639,13 +639,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { // Async public API /// Write. - pub async fn write(&mut self, address: Address, write: &[u8]) -> Result<(), Error> { + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { - self.write_internal(address, write, true, timeout) + self.write_internal(address.into(), write, true, timeout) } else { timeout - .with(self.write_dma_internal(address, write, true, true, timeout)) + .with(self.write_dma_internal(address.into(), write, true, true, timeout)) .await } } @@ -676,32 +676,32 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } /// Read. - pub async fn read(&mut self, address: Address, buffer: &mut [u8]) -> Result<(), Error> { + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); if buffer.is_empty() { - self.read_internal(address, buffer, false, timeout) + self.read_internal(address.into(), buffer, false, timeout) } else { - let fut = self.read_dma_internal(address, buffer, false, timeout); + let fut = self.read_dma_internal(address.into(), buffer, false, timeout); timeout.with(fut).await } } /// Write, restart, read. - pub async fn write_read(&mut self, address: Address, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { - self.write_internal(address, write, false, timeout)?; + self.write_internal(address.into(), write, false, timeout)?; } else { - let fut = self.write_dma_internal(address, write, true, true, timeout); + let fut = self.write_dma_internal(address.into(), write, true, true, timeout); timeout.with(fut).await?; } if read.is_empty() { - self.read_internal(address, read, true, timeout)?; + self.read_internal(address.into(), read, true, timeout)?; } else { - let fut = self.read_dma_internal(address, read, true, timeout); + let fut = self.read_dma_internal(address.into(), read, true, timeout); timeout.with(fut).await?; } @@ -713,7 +713,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub async fn transaction(&mut self, addr: Address, operations: &mut [Operation<'_>]) -> Result<(), Error> { + pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { let _ = addr; let _ = operations; todo!() @@ -1232,91 +1232,3 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { Ok(()) } } - -// ======== Embedded HAL impls ======== - -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> - embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> -where - A: Into
, -{ - type Error = Error; - - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address.into(), buffer) - } -} - -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> - embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> -where - A: Into
, -{ - type Error = Error; - - fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address.into(), write) - } -} - -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode> - embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> -where - A: Into
, -{ - type Error = Error; - - fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address.into(), write, read) - } -} - -impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_1::i2c::AddressMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> -where - Address: From, -{ - fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address.into(), read) - } - - fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address.into(), write) - } - - fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address.into(), write, read) - } - - fn transaction( - &mut self, - address: A, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.blocking_transaction(address.into(), operations) - } -} - -impl<'d, IM: MasterMode, A: embedded_hal_async::i2c::AddressMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> -where - Address: From, -{ - async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { - self.read(address.into(), read).await - } - - async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - self.write(address.into(), write).await - } - - async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.write_read(address.into(), write, read).await - } - - async fn transaction( - &mut self, - address: A, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.transaction(address.into(), operations).await - } -} -- cgit From 50619638ee5c71110430420a04d1b3113eb91945 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 13 Aug 2024 10:39:34 -0600 Subject: fix examples --- embassy-stm32/src/i2c/mod.rs | 3 ++- examples/stm32h7/src/bin/i2c_shared.rs | 2 +- examples/stm32l4/src/bin/spe_adin1110_http_server.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 6f952b8f8..c1911cd06 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -16,7 +16,8 @@ use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use mode::{Master, MasterMode}; +use mode::{MasterMode}; +pub use mode::{Master, MultiMaster}; use crate::dma::ChannelAndRequest; use crate::gpio::{AnyPin, SealedPin as _}; diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 6f4815582..321b35a3e 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs @@ -23,7 +23,7 @@ const SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17]; const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24]; const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98]; -static I2C_BUS: StaticCell>>> = StaticCell::new(); +static I2C_BUS: StaticCell>>> = StaticCell::new(); bind_interrupts!(struct Irqs { I2C1_EV => i2c::EventInterruptHandler; diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index bd633cecb..8f23e4083 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -60,7 +60,7 @@ pub type SpeSpiCs = ExclusiveDevice, Delay>; pub type SpeInt = exti::ExtiInput<'static>; pub type SpeRst = Output<'static>; pub type Adin1110T = ADIN1110; -pub type TempSensI2c = I2c<'static, Async>; +pub type TempSensI2c = I2c<'static, Async, i2c::Master>; static TEMP: AtomicI32 = AtomicI32::new(0); -- cgit From bfc162d43755f988c0b4963fc23386273ce02a35 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 13 Aug 2024 10:48:30 -0600 Subject: fix rustfmt --- embassy-stm32/src/i2c/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index c1911cd06..94507eb34 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -16,7 +16,7 @@ use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use mode::{MasterMode}; +use mode::MasterMode; pub use mode::{Master, MultiMaster}; use crate::dma::ChannelAndRequest; -- cgit From fc342915e6155dec7bafa3e135da7f37a9a07f5c Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 13 Aug 2024 12:53:58 -0600 Subject: add stm32 i2c slave example --- embassy-stm32/src/i2c/mod.rs | 10 +-- embassy-stm32/src/i2c/v2.rs | 26 +++--- examples/stm32g4/src/bin/i2c_slave.rs | 149 ++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 examples/stm32g4/src/bin/i2c_slave.rs diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 94507eb34..2ff21702b 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -70,19 +70,19 @@ pub mod mode { #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// The command kind to the slave from the master -pub enum CommandKind { +pub enum SlaveCommandKind { /// Write to the slave - SlaveReceive, + Write, /// Read from the slave - SlaveSend, + Read, } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// The command kind to the slave from the master and the address that the slave matched -pub struct Command { +pub struct SlaveCommand { /// The kind of command - pub kind: CommandKind, + pub kind: SlaveCommandKind, /// The address that the slave matched pub address: Address, } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 1cc41fb5f..64ccd24c7 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -887,7 +887,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { /// Listen for incoming I2C messages. /// /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. - pub async fn listen(&mut self) -> Result { + pub async fn listen(&mut self) -> Result { let state = self.state; self.info.regs.cr1().modify(|reg| { reg.set_addrie(true); @@ -902,12 +902,12 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { // we do not clear the address flag here as it will be cleared by the dma read/write // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it match isr.dir() { - i2c::vals::Dir::WRITE => Poll::Ready(Ok(Command { - kind: CommandKind::SlaveReceive, + i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand { + kind: SlaveCommandKind::Write, address: self.determine_matched_address()?, })), - i2c::vals::Dir::READ => Poll::Ready(Ok(Command { - kind: CommandKind::SlaveSend, + i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand { + kind: SlaveCommandKind::Read, address: self.determine_matched_address()?, })), } @@ -916,30 +916,30 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { .await } - /// Respond to a receive command. - pub fn blocking_respond_to_receive(&self, read: &mut [u8]) -> Result<(), Error> { + /// Respond to a write command. + pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); self.slave_read_internal(read, timeout) } - /// Respond to a send command. - pub fn blocking_respond_to_send(&mut self, write: &[u8]) -> Result<(), Error> { + /// Respond to a read command. + pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> { let timeout = self.timeout(); self.slave_write_internal(write, timeout) } } impl<'d> I2c<'d, Async, MultiMaster> { - /// Respond to a receive command. + /// Respond to a write command. /// /// Returns the total number of bytes received. - pub async fn respond_to_receive(&mut self, buffer: &mut [u8]) -> Result { + pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { let timeout = self.timeout(); timeout.with(self.read_dma_internal_slave(buffer, timeout)).await } - /// Respond to a send request from an I2C master. - pub async fn respond_to_send(&mut self, write: &[u8]) -> Result { + /// Respond to a read request from an I2C master. + pub async fn respond_to_read(&mut self, write: &[u8]) -> Result { let timeout = self.timeout(); timeout.with(self.write_dma_internal_slave(write, timeout)).await } diff --git a/examples/stm32g4/src/bin/i2c_slave.rs b/examples/stm32g4/src/bin/i2c_slave.rs new file mode 100644 index 000000000..a723a0e18 --- /dev/null +++ b/examples/stm32g4/src/bin/i2c_slave.rs @@ -0,0 +1,149 @@ +//! This example shows how to use an stm32 as both a master and a slave. +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind}; +use embassy_stm32::mode::Async; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C1_ER => i2c::ErrorInterruptHandler; + I2C1_EV => i2c::EventInterruptHandler; + I2C2_ER => i2c::ErrorInterruptHandler; + I2C2_EV => i2c::EventInterruptHandler; +}); + +const DEV_ADDR: u8 = 0x42; + +#[embassy_executor::task] +async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! { + info!("Device start"); + + let mut state = 0; + + loop { + let mut buf = [0u8; 128]; + match dev.listen().await { + Ok(i2c::SlaveCommand { + kind: SlaveCommandKind::Read, + address: Address::SevenBit(DEV_ADDR), + }) => match dev.respond_to_read(&[state]).await { + Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x), + Ok(i2c::SendStatus::Done) => {} + Err(e) => error!("error while responding {}", e), + }, + Ok(i2c::SlaveCommand { + kind: SlaveCommandKind::Write, + address: Address::SevenBit(DEV_ADDR), + }) => match dev.respond_to_write(&mut buf).await { + Ok(len) => { + info!("Device received write: {}", buf[..len]); + + if match buf[0] { + // Set the state + 0xC2 => { + state = buf[1]; + true + } + // Reset State + 0xC8 => { + state = 0; + true + } + x => { + error!("Invalid Write Read {:x}", x); + false + } + } { + match dev.respond_to_read(&[state]).await { + Ok(read_status) => info!( + "This read is part of a write/read transaction. The response read status {}", + read_status + ), + Err(i2c::Error::Timeout) => { + info!("The device only performed a write and it not also do a read") + } + Err(e) => error!("error while responding {}", e), + } + } + } + Err(e) => error!("error while receiving {}", e), + }, + Ok(i2c::SlaveCommand { address, .. }) => { + defmt::unreachable!( + "The slave matched address: {}, which it was not configured for", + address + ); + } + Err(e) => error!("{}", e), + } + } +} + +#[embassy_executor::task] +async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) { + info!("Controller start"); + + loop { + let mut resp_buff = [0u8; 1]; + for i in 0..10 { + match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await { + Ok(_) => { + info!("write_read response: {}", resp_buff); + defmt::assert_eq!(resp_buff[0], i); + } + Err(e) => error!("Error writing {}", e), + } + + Timer::after_millis(100).await; + } + match con.read(DEV_ADDR, &mut resp_buff).await { + Ok(_) => { + info!("read response: {}", resp_buff); + // assert that the state is the last index that was written + defmt::assert_eq!(resp_buff[0], 9); + } + Err(e) => error!("Error writing {}", e), + } + match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await { + Ok(_) => { + info!("write_read response: {}", resp_buff); + // assert that the state has been reset + defmt::assert_eq!(resp_buff[0], 0); + } + Err(e) => error!("Error writing {}", e), + } + Timer::after_millis(100).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let speed = Hertz::khz(400); + let config = i2c::Config::default(); + + let d_addr_config = i2c::SlaveAddrConfig { + addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)), + general_call: false, + }; + let d_sda = p.PA8; + let d_scl = p.PA9; + let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config) + .into_slave_multimaster(d_addr_config); + + unwrap!(spawner.spawn(device_task(device))); + + let c_sda = p.PB8; + let c_scl = p.PB7; + let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config); + + unwrap!(spawner.spawn(controller_task(controller))); +} -- cgit From cb1bccfd5cf528497e3be8e0da9928854ed8e081 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sun, 25 May 2025 21:39:23 +0800 Subject: feat(stm32): Add DAC::new_unbuffered method. Signed-off-by: Ivan Li --- embassy-stm32/src/dac/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 30046849b..d8f1f96f2 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -403,6 +403,46 @@ impl<'d, T: Instance> Dac<'d, T, Async> { Mode::NormalExternalBuffered, ) } + /// Create a new `Dac` instance with external output pins and unbuffered mode. + /// + /// This function consumes the underlying DAC peripheral and allows access to both channels. + /// The channels are configured for external output with the buffer disabled. + /// + /// The channels are enabled on creation and begin to drive their output pins. + /// Note that some methods, such as `set_trigger()` and `set_mode()`, will + /// disable the channel; you must re-enable them with `enable()`. + /// + /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` + /// method on the underlying channels. + /// + /// # Arguments + /// + /// * `peri` - The DAC peripheral instance. + /// * `dma_ch1` - The DMA channel for DAC channel 1. + /// * `dma_ch2` - The DMA channel for DAC channel 2. + /// * `pin_ch1` - The GPIO pin for DAC channel 1 output. + /// * `pin_ch2` - The GPIO pin for DAC channel 2 output. + /// + /// # Returns + /// + /// A new `Dac` instance in unbuffered mode. + pub fn new_unbuffered( + peri: Peri<'d, T>, + dma_ch1: Peri<'d, impl Dma>, + dma_ch2: Peri<'d, impl Dma>, + pin_ch1: Peri<'d, impl DacPin + crate::gpio::Pin>, + pin_ch2: Peri<'d, impl DacPin + crate::gpio::Pin>, + ) -> Self { + pin_ch1.set_as_analog(); + pin_ch2.set_as_analog(); + Self::new_inner( + peri, + new_dma!(dma_ch1), + new_dma!(dma_ch2), + #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] + Mode::NormalExternalUnbuffered, + ) + } /// Create a new `Dac` instance where the external output pins are not used, /// so the DAC can only be used to generate internal signals but the GPIO -- cgit From 035040e53cba24d7a15a055b605d50c568eb9e57 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Tue, 27 May 2025 11:38:11 -0700 Subject: Update layer_by_layer.adoc to clarify PAC limitations proposed fix for #4226, re. https://github.com/embassy-rs/embassy/issues/4226#issuecomment-2908772500 --- docs/pages/layer_by_layer.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/layer_by_layer.adoc b/docs/pages/layer_by_layer.adoc index 7dba11b5e..0692ee4fa 100644 --- a/docs/pages/layer_by_layer.adoc +++ b/docs/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 provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code. +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 little to prevent you from configuring or coordinating those registers incorrectly. 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. -- cgit From a4d4f62a1e0e808ec3dd93e282f517a2f8ad9fa5 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Wed, 28 May 2025 22:00:25 -0500 Subject: Allow `-> impl Future` in #[task] --- embassy-executor-macros/src/macros/task.rs | 34 +++--- embassy-executor/src/lib.rs | 47 +++++++- embassy-executor/tests/test.rs | 24 ++++- embassy-executor/tests/ui.rs | 4 + embassy-executor/tests/ui/bad_return_impl_trait.rs | 9 ++ .../tests/ui/bad_return_impl_trait.stderr | 120 +++++++++++++++++++++ .../tests/ui/bad_return_impl_trait_nightly.rs | 9 ++ .../tests/ui/bad_return_impl_trait_nightly.stderr | 9 ++ 8 files changed, 239 insertions(+), 17 deletions(-) create mode 100644 embassy-executor/tests/ui/bad_return_impl_trait.rs create mode 100644 embassy-executor/tests/ui/bad_return_impl_trait.stderr create mode 100644 embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs create mode 100644 embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 91d6beee8..91bf8e940 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -51,7 +51,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { .embassy_executor .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); - if f.sig.asyncness.is_none() { + let returns_impl_trait = match &f.sig.output { + ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)), + _ => false, + }; + if f.sig.asyncness.is_none() && !returns_impl_trait { error(&mut errors, &f.sig, "task functions must be async"); } if !f.sig.generics.params.is_empty() { @@ -66,17 +70,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { if !f.sig.variadic.is_none() { error(&mut errors, &f.sig, "task functions must not be variadic"); } - match &f.sig.output { - ReturnType::Default => {} - ReturnType::Type(_, ty) => match &**ty { - Type::Tuple(tuple) if tuple.elems.is_empty() => {} - Type::Never(_) => {} - _ => error( - &mut errors, - &f.sig, - "task functions must either not return a value, return `()` or return `!`", - ), - }, + if f.sig.asyncness.is_some() { + match &f.sig.output { + ReturnType::Default => {} + ReturnType::Type(_, ty) => match &**ty { + Type::Tuple(tuple) if tuple.elems.is_empty() => {} + Type::Never(_) => {} + _ => error( + &mut errors, + &f.sig, + "task functions must either not return a value, return `()` or return `!`", + ), + }, + } } let mut args = Vec::new(); @@ -128,12 +134,12 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { #[cfg(feature = "nightly")] let mut task_outer_body = quote! { trait _EmbassyInternalTaskTrait { - type Fut: ::core::future::Future + 'static; + type Fut: ::core::future::Future + 'static; fn construct(#fargs) -> Self::Fut; } impl _EmbassyInternalTaskTrait for () { - type Fut = impl core::future::Future + 'static; + type Fut = impl core::future::Future + 'static; fn construct(#fargs) -> Self::Fut { #task_inner_ident(#(#full_args,)*) } diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index dfe420bab..70abfcc3a 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -65,8 +65,17 @@ pub mod _export { use crate::raw::TaskPool; + trait TaskReturnValue {} + impl TaskReturnValue for () {} + impl TaskReturnValue for Never {} + + #[diagnostic::on_unimplemented( + message = "task function futures must resolve to `()`", + note = "use `async fn` or change the return type to `impl Future`" + )] + #[allow(private_bounds)] pub trait TaskFn: Copy { - type Fut: Future + 'static; + type Fut: Future + 'static; } macro_rules! task_fn_impl { @@ -74,7 +83,7 @@ pub mod _export { impl TaskFn<($($Tn,)*)> for F where F: Copy + FnOnce($($Tn,)*) -> Fut, - Fut: Future + 'static, + Fut: Future + 'static, { type Fut = Fut; } @@ -205,4 +214,38 @@ pub mod _export { Align268435456: 268435456, Align536870912: 536870912, ); + + #[allow(dead_code)] + trait HasOutput { + type Output; + } + + impl HasOutput for fn() -> O { + type Output = O; + } + + #[allow(dead_code)] + type Never = ! as HasOutput>::Output; +} + +/// Implementation details for embassy macros. +/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. +#[doc(hidden)] +#[cfg(feature = "nightly")] +pub mod _export { + pub trait TaskReturnValue {} + impl TaskReturnValue for () {} + impl TaskReturnValue for Never {} + + #[allow(dead_code)] + trait HasOutput { + type Output; + } + + impl HasOutput for fn() -> O { + type Output = O; + } + + #[allow(dead_code)] + type Never = ! as HasOutput>::Output; } diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 78c49c071..c7ff4554c 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] use std::boxed::Box; -use std::future::poll_fn; +use std::future::{poll_fn, Future}; use std::sync::{Arc, Mutex}; use std::task::Poll; @@ -73,6 +73,28 @@ fn executor_task() { ) } +#[test] +fn executor_task_rpit() { + #[task] + fn task1(trace: Trace) -> impl Future { + async move { trace.push("poll task1") } + } + + let (executor, trace) = setup(); + executor.spawner().spawn(task1(trace.clone())).unwrap(); + + unsafe { executor.poll() }; + unsafe { executor.poll() }; + + assert_eq!( + trace.get(), + &[ + "pend", // spawning a task pends the executor + "poll task1", // poll only once. + ] + ) +} + #[test] fn executor_task_self_wake() { #[task] diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs index 278a4b903..ed8228e27 100644 --- a/embassy-executor/tests/ui.rs +++ b/embassy-executor/tests/ui.rs @@ -17,6 +17,10 @@ fn ui() { t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); t.compile_fail("tests/ui/not_async.rs"); + // #[cfg(not(feature = "nightly"))] // output differs on stable and nightly + // t.compile_fail("tests/ui/bad_return_impl_trait.rs"); + #[cfg(feature = "nightly")] + t.compile_fail("tests/ui/bad_return_impl_trait_nightly.rs"); t.compile_fail("tests/ui/self_ref.rs"); t.compile_fail("tests/ui/self.rs"); t.compile_fail("tests/ui/type_error.rs"); diff --git a/embassy-executor/tests/ui/bad_return_impl_trait.rs b/embassy-executor/tests/ui/bad_return_impl_trait.rs new file mode 100644 index 000000000..baaa7dc5a --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_trait.rs @@ -0,0 +1,9 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] +use core::future::Future; + +#[embassy_executor::task] +fn task() -> impl Future { + async { 5 } +} + +fn main() {} diff --git a/embassy-executor/tests/ui/bad_return_impl_trait.stderr b/embassy-executor/tests/ui/bad_return_impl_trait.stderr new file mode 100644 index 000000000..9e2df353e --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_trait.stderr @@ -0,0 +1,120 @@ +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_size` + --> src/lib.rs + | + | pub const fn task_pool_size(_: F) -> usize + | -------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_size` + +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_size` + --> src/lib.rs + | + | pub const fn task_pool_size(_: F) -> usize + | -------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_align` + --> src/lib.rs + | + | pub const fn task_pool_align(_: F) -> usize + | --------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_align` + +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_align` + --> src/lib.rs + | + | pub const fn task_pool_align(_: F) -> usize + | --------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs + | + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_new` + +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs + | + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task function futures must resolve to `()` + --> tests/ui/bad_return_impl_trait.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `__task_pool_get` + --> tests/ui/bad_return_impl_trait.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs new file mode 100644 index 000000000..baaa7dc5a --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs @@ -0,0 +1,9 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] +use core::future::Future; + +#[embassy_executor::task] +fn task() -> impl Future { + async { 5 } +} + +fn main() {} diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr new file mode 100644 index 000000000..21e20d5c0 --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `u32: TaskReturnValue` is not satisfied + --> tests/ui/bad_return_impl_trait_nightly.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32` + | + = help: the following other types implement trait `TaskReturnValue`: + () + ! as _export::HasOutput>::Output -- cgit From dbff432e19b326d7cef109bc4d2a0cd51260562d Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Thu, 29 May 2025 05:18:16 -0500 Subject: Add test for -> impl Future --- embassy-executor/tests/test.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index c7ff4554c..c1e7ec5d7 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -1,4 +1,5 @@ #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] +#![cfg_attr(feature = "nightly", feature(never_type))] use std::boxed::Box; use std::future::{poll_fn, Future}; @@ -58,6 +59,11 @@ fn executor_task() { trace.push("poll task1") } + #[task] + async fn task2() -> ! { + panic!() + } + let (executor, trace) = setup(); executor.spawner().spawn(task1(trace.clone())).unwrap(); @@ -80,6 +86,12 @@ fn executor_task_rpit() { async move { trace.push("poll task1") } } + #[cfg(feature = "nightly")] + #[task] + fn task2() -> impl Future { + async { panic!() } + } + let (executor, trace) = setup(); executor.spawner().spawn(task1(trace.clone())).unwrap(); -- cgit From b06a708f81d208236763a121797807fd5b48aee6 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Thu, 29 May 2025 05:54:25 -0500 Subject: Mention ! in diagnostic --- embassy-executor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 70abfcc3a..e26e8ee7d 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -70,7 +70,7 @@ pub mod _export { impl TaskReturnValue for Never {} #[diagnostic::on_unimplemented( - message = "task function futures must resolve to `()`", + message = "task futures must resolve to `()` or `!`", note = "use `async fn` or change the return type to `impl Future`" )] #[allow(private_bounds)] -- cgit From 0d83fbbb57cf17186a1b8f40f57ef7a35b3e9627 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sun, 1 Jun 2025 10:32:24 -0500 Subject: Add diagnostic::on_unimplemented for nightly --- embassy-executor/src/lib.rs | 4 ++++ embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index e26e8ee7d..e174a0594 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -233,6 +233,10 @@ pub mod _export { #[doc(hidden)] #[cfg(feature = "nightly")] pub mod _export { + #[diagnostic::on_unimplemented( + message = "task futures must resolve to `()` or `!`", + note = "use `async fn` or change the return type to `impl Future`" + )] pub trait TaskReturnValue {} impl TaskReturnValue for () {} impl TaskReturnValue for Never {} diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr index 21e20d5c0..a51251bb8 100644 --- a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr +++ b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr @@ -1,9 +1,10 @@ -error[E0277]: the trait bound `u32: TaskReturnValue` is not satisfied +error[E0277]: task futures must resolve to `()` or `!` --> tests/ui/bad_return_impl_trait_nightly.rs:4:1 | 4 | #[embassy_executor::task] | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32` | + = note: use `async fn` or change the return type to `impl Future` = help: the following other types implement trait `TaskReturnValue`: () ! as _export::HasOutput>::Output -- cgit From 3cef4f0c04a1f0f56c84950d8fd408979299fa50 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sun, 1 Jun 2025 11:42:33 -0500 Subject: Update tests --- embassy-executor/Cargo.toml | 1 + embassy-executor/tests/ui.rs | 13 +- .../tests/ui/bad_return_impl_future.rs | 9 ++ .../tests/ui/bad_return_impl_future.stderr | 120 ++++++++++++++++++ .../tests/ui/bad_return_impl_future_nightly.rs | 9 ++ .../tests/ui/bad_return_impl_future_nightly.stderr | 10 ++ embassy-executor/tests/ui/bad_return_impl_trait.rs | 9 -- .../tests/ui/bad_return_impl_trait.stderr | 120 ------------------ .../tests/ui/bad_return_impl_trait_nightly.rs | 9 -- .../tests/ui/bad_return_impl_trait_nightly.stderr | 10 -- embassy-executor/tests/ui/return_impl_send.rs | 6 + embassy-executor/tests/ui/return_impl_send.stderr | 137 +++++++++++++++++++++ .../tests/ui/return_impl_send_nightly.rs | 6 + .../tests/ui/return_impl_send_nightly.stderr | 10 ++ 14 files changed, 317 insertions(+), 152 deletions(-) create mode 100644 embassy-executor/tests/ui/bad_return_impl_future.rs create mode 100644 embassy-executor/tests/ui/bad_return_impl_future.stderr create mode 100644 embassy-executor/tests/ui/bad_return_impl_future_nightly.rs create mode 100644 embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr delete mode 100644 embassy-executor/tests/ui/bad_return_impl_trait.rs delete mode 100644 embassy-executor/tests/ui/bad_return_impl_trait.stderr delete mode 100644 embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs delete mode 100644 embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr create mode 100644 embassy-executor/tests/ui/return_impl_send.rs create mode 100644 embassy-executor/tests/ui/return_impl_send.stderr create mode 100644 embassy-executor/tests/ui/return_impl_send_nightly.rs create mode 100644 embassy-executor/tests/ui/return_impl_send_nightly.stderr diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index f014ccf30..2dbf2c29a 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -59,6 +59,7 @@ avr-device = { version = "0.7.0", optional = true } critical-section = { version = "1.1", features = ["std"] } trybuild = "1.0" embassy-sync = { path = "../embassy-sync" } +rustversion = "1.0.21" [features] diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs index ed8228e27..c4a1a601c 100644 --- a/embassy-executor/tests/ui.rs +++ b/embassy-executor/tests/ui.rs @@ -17,10 +17,15 @@ fn ui() { t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); t.compile_fail("tests/ui/not_async.rs"); - // #[cfg(not(feature = "nightly"))] // output differs on stable and nightly - // t.compile_fail("tests/ui/bad_return_impl_trait.rs"); - #[cfg(feature = "nightly")] - t.compile_fail("tests/ui/bad_return_impl_trait_nightly.rs"); + if rustversion::cfg!(stable) { + // output is slightly different on nightly + t.compile_fail("tests/ui/bad_return_impl_future.rs"); + t.compile_fail("tests/ui/return_impl_send.rs"); + } + if cfg!(feature = "nightly") { + t.compile_fail("tests/ui/bad_return_impl_future_nightly.rs"); + t.compile_fail("tests/ui/return_impl_send_nightly.rs"); + } t.compile_fail("tests/ui/self_ref.rs"); t.compile_fail("tests/ui/self.rs"); t.compile_fail("tests/ui/type_error.rs"); diff --git a/embassy-executor/tests/ui/bad_return_impl_future.rs b/embassy-executor/tests/ui/bad_return_impl_future.rs new file mode 100644 index 000000000..baaa7dc5a --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future.rs @@ -0,0 +1,9 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] +use core::future::Future; + +#[embassy_executor::task] +fn task() -> impl Future { + async { 5 } +} + +fn main() {} diff --git a/embassy-executor/tests/ui/bad_return_impl_future.stderr b/embassy-executor/tests/ui/bad_return_impl_future.stderr new file mode 100644 index 000000000..2980fd18b --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future.stderr @@ -0,0 +1,120 @@ +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_size` + --> src/lib.rs + | + | pub const fn task_pool_size(_: F) -> usize + | -------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_size` + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_size` + --> src/lib.rs + | + | pub const fn task_pool_size(_: F) -> usize + | -------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_align` + --> src/lib.rs + | + | pub const fn task_pool_align(_: F) -> usize + | --------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_align` + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_align` + --> src/lib.rs + | + | pub const fn task_pool_align(_: F) -> usize + | --------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs + | + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_new` + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs + | + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `__task_pool_get` + --> tests/ui/bad_return_impl_future.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs b/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs new file mode 100644 index 000000000..baaa7dc5a --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs @@ -0,0 +1,9 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] +use core::future::Future; + +#[embassy_executor::task] +fn task() -> impl Future { + async { 5 } +} + +fn main() {} diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr new file mode 100644 index 000000000..73ceb989d --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr @@ -0,0 +1,10 @@ +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future_nightly.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32` + | + = note: use `async fn` or change the return type to `impl Future` + = help: the following other types implement trait `TaskReturnValue`: + () + ! as _export::HasOutput>::Output diff --git a/embassy-executor/tests/ui/bad_return_impl_trait.rs b/embassy-executor/tests/ui/bad_return_impl_trait.rs deleted file mode 100644 index baaa7dc5a..000000000 --- a/embassy-executor/tests/ui/bad_return_impl_trait.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] -use core::future::Future; - -#[embassy_executor::task] -fn task() -> impl Future { - async { 5 } -} - -fn main() {} diff --git a/embassy-executor/tests/ui/bad_return_impl_trait.stderr b/embassy-executor/tests/ui/bad_return_impl_trait.stderr deleted file mode 100644 index 9e2df353e..000000000 --- a/embassy-executor/tests/ui/bad_return_impl_trait.stderr +++ /dev/null @@ -1,120 +0,0 @@ -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:5:4 - | -4 | #[embassy_executor::task] - | ------------------------- required by a bound introduced by this call -5 | fn task() -> impl Future { - | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_size` - --> src/lib.rs - | - | pub const fn task_pool_size(_: F) -> usize - | -------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^ required by this bound in `task_pool_size` - -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:4:1 - | -4 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_size` - --> src/lib.rs - | - | pub const fn task_pool_size(_: F) -> usize - | -------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size` - = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:5:4 - | -4 | #[embassy_executor::task] - | ------------------------- required by a bound introduced by this call -5 | fn task() -> impl Future { - | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_align` - --> src/lib.rs - | - | pub const fn task_pool_align(_: F) -> usize - | --------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^ required by this bound in `task_pool_align` - -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:4:1 - | -4 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_align` - --> src/lib.rs - | - | pub const fn task_pool_align(_: F) -> usize - | --------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` - = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:5:4 - | -4 | #[embassy_executor::task] - | ------------------------- required by a bound introduced by this call -5 | fn task() -> impl Future { - | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_new` - --> src/lib.rs - | - | pub const fn task_pool_new(_: F) -> TaskPool - | ------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^ required by this bound in `task_pool_new` - -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:4:1 - | -4 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_new` - --> src/lib.rs - | - | pub const fn task_pool_new(_: F) -> TaskPool - | ------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` - = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: task function futures must resolve to `()` - --> tests/ui/bad_return_impl_trait.rs:5:4 - | -4 | #[embassy_executor::task] - | ------------------------- required by a bound introduced by this call -5 | fn task() -> impl Future { - | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `__task_pool_get` - --> tests/ui/bad_return_impl_trait.rs:4:1 - | -4 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` - = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs deleted file mode 100644 index baaa7dc5a..000000000 --- a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] -use core::future::Future; - -#[embassy_executor::task] -fn task() -> impl Future { - async { 5 } -} - -fn main() {} diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr deleted file mode 100644 index a51251bb8..000000000 --- a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error[E0277]: task futures must resolve to `()` or `!` - --> tests/ui/bad_return_impl_trait_nightly.rs:4:1 - | -4 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32` - | - = note: use `async fn` or change the return type to `impl Future` - = help: the following other types implement trait `TaskReturnValue`: - () - ! as _export::HasOutput>::Output diff --git a/embassy-executor/tests/ui/return_impl_send.rs b/embassy-executor/tests/ui/return_impl_send.rs new file mode 100644 index 000000000..6ddb0e722 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +fn task() -> impl Send {} + +fn main() {} diff --git a/embassy-executor/tests/ui/return_impl_send.stderr b/embassy-executor/tests/ui/return_impl_send.stderr new file mode 100644 index 000000000..7e3e468b8 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send.stderr @@ -0,0 +1,137 @@ +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:4:4 + | +3 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +4 | fn task() -> impl Send {} + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_size` + --> src/lib.rs + | + | pub const fn task_pool_size(_: F) -> usize + | -------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_size` + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:3:1 + | +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_size` + --> src/lib.rs + | + | pub const fn task_pool_size(_: F) -> usize + | -------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:4:4 + | +3 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +4 | fn task() -> impl Send {} + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_align` + --> src/lib.rs + | + | pub const fn task_pool_align(_: F) -> usize + | --------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_align` + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:3:1 + | +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_align` + --> src/lib.rs + | + | pub const fn task_pool_align(_: F) -> usize + | --------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:4:4 + | +3 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +4 | fn task() -> impl Send {} + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs + | + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_new` + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:3:1 + | +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs + | + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/return_impl_send.rs:4:4 + | +3 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +4 | fn task() -> impl Send {} + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `__task_pool_get` + --> tests/ui/return_impl_send.rs:3:1 + | +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `impl Send` is not a future + --> tests/ui/return_impl_send.rs:3:1 + | +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future + | + = help: the trait `Future` is not implemented for `impl Send` +note: required by a bound in `TaskPool::::_spawn_async_fn` + --> src/raw/mod.rs + | + | impl TaskPool { + | ^^^^^^ required by this bound in `TaskPool::::_spawn_async_fn` +... + | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + | --------------- required by a bound in this associated function + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-executor/tests/ui/return_impl_send_nightly.rs b/embassy-executor/tests/ui/return_impl_send_nightly.rs new file mode 100644 index 000000000..6ddb0e722 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send_nightly.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +fn task() -> impl Send {} + +fn main() {} diff --git a/embassy-executor/tests/ui/return_impl_send_nightly.stderr b/embassy-executor/tests/ui/return_impl_send_nightly.stderr new file mode 100644 index 000000000..de9ba6243 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send_nightly.stderr @@ -0,0 +1,10 @@ +error[E0277]: `impl Send` is not a future + --> tests/ui/return_impl_send_nightly.rs:3:1 + | +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `impl Send` is not a future + | return type was inferred to be `impl Send` here + | + = help: the trait `Future` is not implemented for `impl Send` -- cgit From c10688cd40d5ffdc181446f304e833ec880b25f0 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Fri, 6 Jun 2025 13:01:11 -0400 Subject: Add public function to get current clock configuration --- embassy-stm32/src/rcc/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 3733fed56..c41f81816 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -95,6 +95,15 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() } +/// Get the current clock configuration of the chip. +pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clocks { + // Safety: the existence of a `Peri` means that `rcc::init()` + // has already been called, so `CLOCK_FREQS` must be initialized. + // The clocks could be modified again by `reinit()`, but reinit + // (for this reason) requires an exclusive reference to `Peri`. + unsafe { get_freqs() } +} + pub(crate) trait SealedRccPeripheral { fn frequency() -> Hertz; #[allow(dead_code)] @@ -381,7 +390,7 @@ pub fn disable() { /// /// This should only be called after `init`. #[cfg(not(feature = "_dual-core"))] -pub fn reinit(config: Config) { +pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { critical_section::with(|cs| init_rcc(cs, config)) } -- cgit From 358a0cd464c0798c7d6a1178b5a633f5d90ad515 Mon Sep 17 00:00:00 2001 From: Adam Newell Date: Mon, 9 Jun 2025 17:15:24 -0400 Subject: Fix MPU region enablement in stack guard installation Updated the MPU region enablement logic in the `install_stack_guard` function to correctly set the region limit by using the stack bottom address plus 256 minus one, ensuring proper memory protection configuration. See Table 235. MPU_RLAR Register in RP2350 documentation See Section 4.5 MPU_RLAR in armv8m MPU documentation --- 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 f3c5a35bb..94b20bce3 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -567,7 +567,7 @@ unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { unsafe { core.MPU.ctrl.write(5); // enable mpu with background default map core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address - core.MPU.rlar.write(1); // enable region + core.MPU.rlar.write(((stack_bottom as usize + 255) as u32) | 1); } Ok(()) } -- cgit From 3b2dd2bad846f237c33f60013ef2a880489c55c9 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 10 Jun 2025 15:49:31 -0600 Subject: fmt --- embassy-stm32/src/i2c/v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 990ce7d21..48008b695 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -48,7 +48,7 @@ pub(crate) unsafe fn on_interrupt() { regs.cr1().modify(|w| { w.set_addrie(false); w.set_stopie(false); - // The flag can only be cleared by writting to nbytes, we won't do that here + // The flag can only be cleared by writting to nbytes, we won't do that here w.set_tcie(false); // Error flags are to be read in the routines, so we also don't clear them here w.set_nackie(false); -- cgit From fb711395222509c80877b8f74a267c258f03ffae Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 10 Jun 2025 15:49:31 -0600 Subject: fix name change --- embassy-stm32/src/i2c/v2.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 48008b695..35dc91c86 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -15,7 +15,7 @@ use crate::pac::i2c; impl From for Oamsk { fn from(value: AddrMask) -> Self { match value { - AddrMask::NOMASK => Oamsk::NOMASK, + AddrMask::NOMASK => Oamsk::NO_MASK, AddrMask::MASK1 => Oamsk::MASK1, AddrMask::MASK2 => Oamsk::MASK2, AddrMask::MASK3 => Oamsk::MASK3, @@ -871,7 +871,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { assert!(length < 256); let reload = if reload { - i2c::vals::Reload::NOTCOMPLETED + i2c::vals::Reload::NOT_COMPLETED } else { i2c::vals::Reload::COMPLETED }; @@ -1295,4 +1295,3 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { Ok(()) } } - -- cgit From 4efb3b4f3f0178b98ea25f3957393ab6977c89de Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 10 Jun 2025 15:58:02 -0600 Subject: fix ci --- examples/stm32h7/src/bin/i2c_shared.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index ec5757284..655ff901f 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs @@ -33,7 +33,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { +async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) { let mut data = [0u8; 2]; loop { @@ -50,7 +50,7 @@ async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Asyn } #[embassy_executor::task] -async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { +async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) { let mut data = [0u8; 6]; loop { -- cgit From 5534a3650755ba7e5d406eb5c78d4f5f0d4d3716 Mon Sep 17 00:00:00 2001 From: Alan Rosenthal Date: Tue, 10 Jun 2025 18:46:41 -0400 Subject: Add RTC example for STM32C0 Tested on STM32C0116F6 Requries: https://github.com/embassy-rs/stm32-data/pull/617 --- embassy-stm32/Cargo.toml | 4 ++-- examples/stm32c0/Cargo.toml | 3 ++- examples/stm32c0/src/bin/rtc.rs | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 examples/stm32c0/src/bin/rtc.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 034f51df9..1031baa2d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-778e3d102186ebb12a8c8b60b7cafdd15858bab3" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-778e3d102186ebb12a8c8b60b7cafdd15858bab3", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 4cf07cef4..70a7b0895 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32c031c6 to your chip name, if necessary. -embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } +embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } @@ -19,6 +19,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "1.0.0", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } +chrono = { version = "^0.4", default-features = false} [profile.release] debug = 2 diff --git a/examples/stm32c0/src/bin/rtc.rs b/examples/stm32c0/src/bin/rtc.rs new file mode 100644 index 000000000..82d8a37ba --- /dev/null +++ b/examples/stm32c0/src/bin/rtc.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = Config::default(); + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + loop { + let now: NaiveDateTime = rtc.now().unwrap().into(); + + info!("{}", now.and_utc().timestamp()); + + Timer::after_millis(1000).await; + } +} -- cgit From 56d76aeb7bccfebf07f0ad5ad9da8d9d36b0865d Mon Sep 17 00:00:00 2001 From: Jakob Date: Wed, 11 Jun 2025 09:08:55 +0200 Subject: Remove incorrect addition of 1 to get_max_duty --- embassy-stm32/src/timer/complementary_pwm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 8eec6c0c7..d51b5a8fc 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -121,7 +121,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() as u16 + 1 + self.inner.get_max_compare_value() as u16 } /// Set the duty for a given channel. -- cgit From 0ee77f50aacdc39aabae1881304a7c744adc24c5 Mon Sep 17 00:00:00 2001 From: Jakob Date: Wed, 11 Jun 2025 09:24:00 +0200 Subject: Add separate case for center aligned mode --- embassy-stm32/src/timer/complementary_pwm.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index d51b5a8fc..1a840650a 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -121,7 +121,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() as u16 + if self.inner.get_counting_mode().is_center_aligned() { + self.inner.get_max_compare_value() as u16 + } else { + self.inner.get_max_compare_value() as u16 + 1 + } } /// Set the duty for a given channel. -- cgit From 66296f673b05f3e34a85f85f30a23a98a863b3e3 Mon Sep 17 00:00:00 2001 From: Jakob Date: Wed, 11 Jun 2025 11:34:37 +0200 Subject: Enable autoreload_preload for complementary PWM --- embassy-stm32/src/timer/complementary_pwm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 8eec6c0c7..7807bb1bf 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -88,6 +88,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); this.inner.set_output_compare_preload(channel, true); }); + this.inner.set_autoreload_preload(true); this } -- cgit From 09967b71f509bdebaf23ab11e3c362e447722240 Mon Sep 17 00:00:00 2001 From: Jakob Date: Wed, 11 Jun 2025 11:48:39 +0200 Subject: Also update the AdvancedInstace4Channel version --- embassy-stm32/src/timer/complementary_pwm.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 1a840650a..fa6164bdd 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -175,7 +175,11 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< } fn get_max_duty(&self) -> Self::Duty { - self.inner.get_max_compare_value() as u16 + 1 + if self.inner.get_counting_mode().is_center_aligned() { + self.inner.get_max_compare_value() as u16 + } else { + self.inner.get_max_compare_value() as u16 + 1 + } } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { -- cgit From f2266242043c0933e6d39e922400b21d726c9be3 Mon Sep 17 00:00:00 2001 From: Annie Ehler Date: Wed, 11 Jun 2025 19:37:37 -0700 Subject: Add extra methods for the low-power interrupt timer. --- embassy-stm32/src/lptim/timer/mod.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/embassy-stm32/src/lptim/timer/mod.rs b/embassy-stm32/src/lptim/timer/mod.rs index a629be62b..f6abd4a74 100644 --- a/embassy-stm32/src/lptim/timer/mod.rs +++ b/embassy-stm32/src/lptim/timer/mod.rs @@ -82,6 +82,31 @@ impl<'d, T: Instance> Timer<'d, T> { pub fn get_max_compare_value(&self) -> u16 { T::regs().arr().read().arr() } + + /// Enable the timer interrupt. + pub fn enable_interrupt(&self) { + T::regs().dier().modify(|w| w.set_arrmie(true)); + } + + /// Disable the timer interrupt. + pub fn disable_interrupt(&self) { + T::regs().dier().modify(|w| w.set_arrmie(false)); + } + + /// Check if the timer interrupt is enabled. + pub fn is_interrupt_enabled(&self) -> bool { + T::regs().dier().read().arrmie() + } + + /// Check if the timer interrupt is pending. + pub fn is_interrupt_pending(&self) -> bool { + T::regs().isr().read().arrm() + } + + /// Clear the timer interrupt. + pub fn clear_interrupt(&self) { + T::regs().icr().write(|w| w.set_arrmcf(true)); + } } #[cfg(any(lptim_v2a, lptim_v2b))] -- cgit From 4301016f155fd26a3934fb9e7bd2920107a32910 Mon Sep 17 00:00:00 2001 From: Annie Ehler Date: Thu, 12 Jun 2025 04:11:49 +0000 Subject: Move the methods to the cfg gated impls to handle register renaming. --- embassy-stm32/src/lptim/timer/mod.rs | 75 ++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/embassy-stm32/src/lptim/timer/mod.rs b/embassy-stm32/src/lptim/timer/mod.rs index f6abd4a74..648da5940 100644 --- a/embassy-stm32/src/lptim/timer/mod.rs +++ b/embassy-stm32/src/lptim/timer/mod.rs @@ -82,31 +82,6 @@ impl<'d, T: Instance> Timer<'d, T> { pub fn get_max_compare_value(&self) -> u16 { T::regs().arr().read().arr() } - - /// Enable the timer interrupt. - pub fn enable_interrupt(&self) { - T::regs().dier().modify(|w| w.set_arrmie(true)); - } - - /// Disable the timer interrupt. - pub fn disable_interrupt(&self) { - T::regs().dier().modify(|w| w.set_arrmie(false)); - } - - /// Check if the timer interrupt is enabled. - pub fn is_interrupt_enabled(&self) -> bool { - T::regs().dier().read().arrmie() - } - - /// Check if the timer interrupt is pending. - pub fn is_interrupt_pending(&self) -> bool { - T::regs().isr().read().arrm() - } - - /// Clear the timer interrupt. - pub fn clear_interrupt(&self) { - T::regs().icr().write(|w| w.set_arrmcf(true)); - } } #[cfg(any(lptim_v2a, lptim_v2b))] @@ -140,6 +115,31 @@ impl<'d, T: Instance> Timer<'d, T> { .ccmr(0) .modify(|w| w.set_ccsel(channel.index(), direction.into())); } + + /// Enable the timer interrupt. + pub fn enable_interrupt(&self) { + T::regs().dier().modify(|w| w.set_arrmie(true)); + } + + /// Disable the timer interrupt. + pub fn disable_interrupt(&self) { + T::regs().dier().modify(|w| w.set_arrmie(false)); + } + + /// Check if the timer interrupt is enabled. + pub fn is_interrupt_enabled(&self) -> bool { + T::regs().dier().read().arrmie() + } + + /// Check if the timer interrupt is pending. + pub fn is_interrupt_pending(&self) -> bool { + T::regs().isr().read().arrm() + } + + /// Clear the timer interrupt. + pub fn clear_interrupt(&self) { + T::regs().icr().write(|w| w.set_arrmcf(true)); + } } #[cfg(not(any(lptim_v2a, lptim_v2b)))] @@ -153,4 +153,29 @@ impl<'d, T: Instance> Timer<'d, T> { pub fn get_compare_value(&self) -> u16 { T::regs().cmp().read().cmp() } + + /// Enable the timer interrupt. + pub fn enable_interrupt(&self) { + T::regs().ier().modify(|w| w.set_arrmie(true)); + } + + /// Disable the timer interrupt. + pub fn disable_interrupt(&self) { + T::regs().ier().modify(|w| w.set_arrmie(false)); + } + + /// Check if the timer interrupt is enabled. + pub fn is_interrupt_enabled(&self) -> bool { + T::regs().ier().read().arrmie() + } + + /// Check if the timer interrupt is pending. + pub fn is_interrupt_pending(&self) -> bool { + T::regs().isr().read().arrm() + } + + /// Clear the timer interrupt. + pub fn clear_interrupt(&self) { + T::regs().icr().write(|w| w.set_arrmcf(true)); + } } -- cgit From a0d17ea5ca0bd76ef4d4398c28bc8f98c4e50065 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 16 Jun 2025 13:57:19 +0200 Subject: Remove futures-util where unnecessary --- embassy-sync/Cargo.toml | 2 +- embassy-sync/src/channel.rs | 4 ++-- embassy-sync/src/pubsub/subscriber.rs | 2 +- embassy-time/Cargo.toml | 2 +- embassy-time/src/timer.rs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 99962f9f6..1e2ea8ea1 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } futures-sink = { version = "0.3", default-features = false, features = [] } -futures-util = { version = "0.3.17", default-features = false } +futures-core = { version = "0.3.31", default-features = false } critical-section = "1.1" heapless = "0.8" cfg-if = "1.0.0" diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 856551417..dda91c651 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -443,7 +443,7 @@ where } } -impl<'ch, M, T, const N: usize> futures_util::Stream for Receiver<'ch, M, T, N> +impl<'ch, M, T, const N: usize> futures_core::Stream for Receiver<'ch, M, T, N> where M: RawMutex, { @@ -962,7 +962,7 @@ where } } -impl futures_util::Stream for Channel +impl futures_core::Stream for Channel where M: RawMutex, { diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index 6ad660cb3..649382cf1 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs @@ -115,7 +115,7 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {} /// Warning: The stream implementation ignores lag results and returns all messages. /// This might miss some messages without you knowing it. -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> futures_util::Stream for Sub<'a, PSB, T> { +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> futures_core::Stream for Sub<'a, PSB, T> { type Item = T; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 76983d880..2284906b0 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -427,7 +427,7 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } -futures-util = { version = "0.3.17", default-features = false } +futures-core = { version = "0.3.31", default-features = false } critical-section = "1.1" cfg-if = "1.0.0" diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index d1162eadd..d3f1e1621 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -2,8 +2,8 @@ use core::future::{poll_fn, Future}; use core::pin::Pin; use core::task::{Context, Poll}; -use futures_util::stream::FusedStream; -use futures_util::Stream; +use futures_core::stream::FusedStream; +use futures_core::Stream; use crate::{Duration, Instant}; -- cgit From 2613295111e47d8476e4b5b26517e7f7900a4ab8 Mon Sep 17 00:00:00 2001 From: Rob Wells Date: Mon, 16 Jun 2025 13:11:46 +0100 Subject: embassy-rp: fix rom_data module documentation Removes module doc comment markers that appeared in the generated documentation. --- embassy-rp/src/rom_data/mod.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/rom_data/mod.rs b/embassy-rp/src/rom_data/mod.rs index e5fcf8e3c..a4aba5737 100644 --- a/embassy-rp/src/rom_data/mod.rs +++ b/embassy-rp/src/rom_data/mod.rs @@ -1,29 +1,29 @@ #![cfg_attr( feature = "rp2040", doc = r" -//! 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. +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. " )] #![cfg_attr( feature = "_rp235x", doc = r" -//! Functions and data from the RPI Bootrom. -//! -//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the -//! RP2350 datasheet: -//! -//! > Whilst some ROM space is dedicated to the implementation of the boot -//! > sequence and USB/UART boot interfaces, the bootrom also contains public -//! > functions that provide useful RP2350 functionality that may be useful for -//! > any code or runtime running on the device +Functions and data from the RPI Bootrom. + +From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the +RP2350 datasheet: + +> Whilst some ROM space is dedicated to the implementation of the boot +> sequence and USB/UART boot interfaces, the bootrom also contains public +> functions that provide useful RP2350 functionality that may be useful for +> any code or runtime running on the device " )] -- cgit From e155d17328b3015a38734df3b540ac0881b05d1c Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 16 Jun 2025 17:55:29 -0700 Subject: sdmmc: set datatime during initialization --- embassy-stm32/src/sdmmc/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 6a02aae70..675d1813b 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -751,7 +751,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Self::wait_idle(); Self::clear_interrupt_flags(); - regs.dtimer().write(|w| w.set_datatime(config.data_transfer_timeout)); regs.dlenr().write(|w| w.set_datalength(length_bytes)); #[cfg(sdmmc_v1)] @@ -789,8 +788,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Self::wait_idle(); Self::clear_interrupt_flags(); - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); regs.dlenr().write(|w| w.set_datalength(length_bytes)); #[cfg(sdmmc_v1)] @@ -1349,6 +1346,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { #[cfg(sdmmc_v1)] w.set_bypass(_bypass); }); + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); Self::cmd(common_cmd::idle(), false)?; -- cgit From 7fe4547ecb8a2838b26e6e0a902fbef3a4d22e52 Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Tue, 17 Jun 2025 14:27:37 +0200 Subject: U5: Apply auto-calibration on MSIK and calculate frequencies for detuned LSE input --- embassy-stm32/src/rcc/u5.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 97eb2eb6d..7e5001499 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -5,7 +5,7 @@ pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; -use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; +use crate::pac::rcc::vals::{Hseext, Msipllsel, Msirgsel, Pllmboost, Pllrge}; #[cfg(all(peri_usb_otg_hs))] pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; use crate::pac::{FLASH, PWR, RCC}; @@ -131,7 +131,18 @@ pub(crate) unsafe fn init(config: Config) { PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); while !PWR.vosr().read().vosrdy() {} - let msis = config.msis.map(|range| { + let lse_calibration_freq = match config.ls.lse { + Some(lse_config) => { + if lse_config.peripherals_clocked && (31_000..=34_000).contains(&lse_config.frequency.0) { + Some(lse_config.frequency) + } else { + None + } + } + _ => None, + }; + + let mut msis = config.msis.map(|range| { // Check MSI output per RM0456 § 11.4.10 match config.voltage_range { VoltageScale::RANGE4 => { @@ -184,8 +195,25 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().modify(|w| { w.set_msikon(true); }); + if lse_calibration_freq.is_some() { + // Enable the MSIK auto-calibration feature + RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK)); + RCC.cr().modify(|w| w.set_msipllen(true)); + } while !RCC.cr().read().msikrdy() {} - msirange_to_hertz(range) + if let Some(freq) = lse_calibration_freq { + // Estimate frequency based on it being a fixed fractional multiple of LSE + let base = msirange_to_hertz(range).0; + let multiplier = (base + 8096) / 16384; + let msik_freq = Hertz(freq.0 * multiplier / 2); + if config.msis == config.msik { + // If MSIS and MSIK are the same range both will be auto calibrated to the same frequency + msis = Some(msik_freq) + } + msik_freq + } else { + msirange_to_hertz(range) + } }); let hsi = config.hsi.then(|| { -- cgit From d750a8b6b99d7012a1aab21ab67ba530e7a7206b Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Tue, 17 Jun 2025 15:46:32 +0200 Subject: Make more accurate table based MSI frequency calculation based on datasheet. --- embassy-stm32/src/rcc/u5.rs | 53 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 7e5001499..e7fe50f33 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -202,10 +202,7 @@ pub(crate) unsafe fn init(config: Config) { } while !RCC.cr().read().msikrdy() {} if let Some(freq) = lse_calibration_freq { - // Estimate frequency based on it being a fixed fractional multiple of LSE - let base = msirange_to_hertz(range).0; - let multiplier = (base + 8096) / 16384; - let msik_freq = Hertz(freq.0 * multiplier / 2); + let msik_freq = calculate_calibrated_msi_frequency(range, freq); if config.msis == config.msik { // If MSIS and MSIK are the same range both will be auto calibrated to the same frequency msis = Some(msik_freq) @@ -542,3 +539,51 @@ fn init_pll(instance: PllInstance, config: Option, input: &PllInput, voltag PllOutput { p, q, r } } + +/// Fraction structure for MSI auto-calibration +/// Represents the multiplier as numerator/denominator that LSE frequency is multiplied by +#[derive(Debug, Clone, Copy)] +struct MsiFraction { + numerator: u32, + denominator: u32, +} + +impl MsiFraction { + const fn new(numerator: u32, denominator: u32) -> Self { + Self { numerator, denominator } + } + + /// Calculate the calibrated frequency given an LSE frequency + fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz { + Hertz(lse_freq.0 * self.numerator / self.denominator) + } +} + +/// Get the calibration fraction for a given MSI range +/// Based on STM32U5 datasheet table for LSE = 32.768 kHz +fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { + match range { + Msirange::RANGE_48MHZ => MsiFraction::new(1465, 1), // Range 0: 48.005 MHz + Msirange::RANGE_24MHZ => MsiFraction::new(1465, 2), // Range 1: 24.003 MHz + Msirange::RANGE_16MHZ => MsiFraction::new(1465, 3), // Range 2: 16.002 MHz + Msirange::RANGE_12MHZ => MsiFraction::new(1465, 4), // Range 3: 12.001 MHz + Msirange::RANGE_4MHZ => MsiFraction::new(122, 1), // Range 4: 3.998 MHz + Msirange::RANGE_2MHZ => MsiFraction::new(61, 1), // Range 5: 1.999 MHz + Msirange::RANGE_1_33MHZ => MsiFraction::new(122, 3), // Range 6: 1.333 MHz + Msirange::RANGE_1MHZ => MsiFraction::new(61, 2), // Range 7: 0.999 MHz + Msirange::RANGE_3_072MHZ => MsiFraction::new(94, 1), // Range 8: 3.08 MHz + Msirange::RANGE_1_536MHZ => MsiFraction::new(47, 1), // Range 9: 1.54 MHz + Msirange::RANGE_1_024MHZ => MsiFraction::new(94, 3), // Range 10: 1.027 MHz + Msirange::RANGE_768KHZ => MsiFraction::new(47, 2), // Range 11: 0.77 MHz + Msirange::RANGE_400KHZ => MsiFraction::new(12, 1), // Range 12: 393 kHz + Msirange::RANGE_200KHZ => MsiFraction::new(6, 1), // Range 13: 196.6 kHz + Msirange::RANGE_133KHZ => MsiFraction::new(4, 1), // Range 14: 131 kHz + Msirange::RANGE_100KHZ => MsiFraction::new(3, 1), // Range 15: 98.3 kHz + } +} + +/// Calculate the calibrated MSI frequency for a given range and LSE frequency +fn calculate_calibrated_msi_frequency(range: Msirange, lse_freq: Hertz) -> Hertz { + let fraction = get_msi_calibration_fraction(range); + fraction.calculate_frequency(lse_freq) +} -- cgit From 905aed45f9c15eef9bf9afa5a6b81ae345694581 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Wed, 18 Jun 2025 15:37:05 -0700 Subject: add tests illustrating the problem --- embassy-sync/Cargo.toml | 1 + embassy-sync/tests/ui.rs | 13 +++++++++++++ embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs | 15 +++++++++++++++ embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs | 6 ++++++ embassy-sync/tests/ui/sync_impl/once_lock.rs | 6 ++++++ 5 files changed, 41 insertions(+) create mode 100644 embassy-sync/tests/ui.rs create mode 100644 embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs create mode 100644 embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs create mode 100644 embassy-sync/tests/ui/sync_impl/once_lock.rs diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 1e2ea8ea1..9e5c39f5e 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -43,3 +43,4 @@ futures-util = { version = "0.3.17", features = [ "channel", "sink" ] } # Enable critical-section implementation for std, for tests critical-section = { version = "1.1", features = ["std"] } static_cell = { version = "2" } +trybuild = "1.0.105" diff --git a/embassy-sync/tests/ui.rs b/embassy-sync/tests/ui.rs new file mode 100644 index 000000000..9ef0d4c67 --- /dev/null +++ b/embassy-sync/tests/ui.rs @@ -0,0 +1,13 @@ +// #[cfg(not(miri))] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + + // These test cases should fail to compile since OnceLock and LazyLock should not unconditionally implement sync + // for all types. These tests are regression tests against the following issues: + // * https://github.com/embassy-rs/embassy/issues/4307 + // * https://github.com/embassy-rs/embassy/issues/3904 + t.compile_fail("tests/ui/sync_impl/lazy_lock_function.rs"); + t.compile_fail("tests/ui/sync_impl/lazy_lock_type.rs"); + t.compile_fail("tests/ui/sync_impl/once_lock.rs"); +} diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs new file mode 100644 index 000000000..c6e6f7e64 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs @@ -0,0 +1,15 @@ +use embassy_sync::lazy_lock::LazyLock; + +fn main() { + let x = 128u8; + let x_ptr: *const u8 = core::ptr::addr_of!(x); + + let closure_capturing_non_sync_variable = || { + unsafe { + core::ptr::read(x_ptr) + } + }; + + // This should fail to compile because the closure captures a non-Sync variable: x_ptr. + let _lazy_u8: LazyLock = LazyLock::new(closure_capturing_non_sync_variable); +} diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs new file mode 100644 index 000000000..4e1383143 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs @@ -0,0 +1,6 @@ +use embassy_sync::lazy_lock::LazyLock; + +// *mut u8 is not Sync, so LazyLock should not implement Sync for this type. This should fail to compile. +static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut()); + +fn main() {} diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.rs b/embassy-sync/tests/ui/sync_impl/once_lock.rs new file mode 100644 index 000000000..8f50d583b --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/once_lock.rs @@ -0,0 +1,6 @@ +use embassy_sync::once_lock::OnceLock; + +// *mut u8 is not Sync, so OnceLock should not implement Sync for this type. This should fail to compile. +static GLOBAL: OnceLock<*mut u8> = OnceLock::new(); + +fn main() {} -- cgit From 051c63fea2fdb9cc9773c3bd311e6c423f3d1cd2 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Wed, 18 Jun 2025 15:38:57 -0700 Subject: fix missing sync bounds --- embassy-sync/src/lazy_lock.rs | 7 ++++++- embassy-sync/src/once_lock.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/embassy-sync/src/lazy_lock.rs b/embassy-sync/src/lazy_lock.rs index 18e3c2019..f1bd88b61 100644 --- a/embassy-sync/src/lazy_lock.rs +++ b/embassy-sync/src/lazy_lock.rs @@ -31,7 +31,12 @@ union Data { f: ManuallyDrop, } -unsafe impl Sync for LazyLock {} +unsafe impl Sync for LazyLock +where + T: Sync, + F: Sync, +{ +} impl T> LazyLock { /// Create a new uninitialized `StaticLock`. diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs index cd05b986d..1e848685a 100644 --- a/embassy-sync/src/once_lock.rs +++ b/embassy-sync/src/once_lock.rs @@ -42,7 +42,7 @@ pub struct OnceLock { data: Cell>, } -unsafe impl Sync for OnceLock {} +unsafe impl Sync for OnceLock where T: Sync {} impl OnceLock { /// Create a new uninitialized `OnceLock`. -- cgit From 3a432920978dc48f0b4c0a8da7c118415a0708fd Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Wed, 18 Jun 2025 16:16:06 -0700 Subject: commit expected errors --- .../tests/ui/sync_impl/lazy_lock_function.rs | 12 ++++------- .../tests/ui/sync_impl/lazy_lock_function.stderr | 24 ++++++++++++++++++++++ .../tests/ui/sync_impl/lazy_lock_type.stderr | 9 ++++++++ embassy-sync/tests/ui/sync_impl/once_lock.stderr | 9 ++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr create mode 100644 embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr create mode 100644 embassy-sync/tests/ui/sync_impl/once_lock.stderr diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs index c6e6f7e64..35f5587c0 100644 --- a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs @@ -3,13 +3,9 @@ use embassy_sync::lazy_lock::LazyLock; fn main() { let x = 128u8; let x_ptr: *const u8 = core::ptr::addr_of!(x); + let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; - let closure_capturing_non_sync_variable = || { - unsafe { - core::ptr::read(x_ptr) - } - }; - - // This should fail to compile because the closure captures a non-Sync variable: x_ptr. - let _lazy_u8: LazyLock = LazyLock::new(closure_capturing_non_sync_variable); + check_sync(LazyLock::new(closure_capturing_non_sync_variable)); } + +fn check_sync(_lazy_lock: T) {} diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr new file mode 100644 index 000000000..daf79ad28 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr @@ -0,0 +1,24 @@ +error[E0277]: `*const u8` cannot be shared between threads safely + --> tests/ui/sync_impl/lazy_lock_function.rs:8:16 + | +6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; + | -- within this `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}` +7 | +8 | check_sync(LazyLock::new(closure_capturing_non_sync_variable)); + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: within `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`, the trait `Sync` is not implemented for `*const u8` + = note: required because it appears within the type `&*const u8` +note: required because it's used within this closure + --> tests/ui/sync_impl/lazy_lock_function.rs:6:47 + | +6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; + | ^^ + = note: required for `embassy_sync::lazy_lock::LazyLock` to implement `Sync` +note: required by a bound in `check_sync` + --> tests/ui/sync_impl/lazy_lock_function.rs:11:18 + | +11 | fn check_sync(_lazy_lock: T) {} + | ^^^^ required by this bound in `check_sync` diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr new file mode 100644 index 000000000..1ccc54c7a --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr @@ -0,0 +1,9 @@ +error[E0277]: `*mut u8` cannot be shared between threads safely + --> tests/ui/sync_impl/lazy_lock_type.rs:4:16 + | +4 | static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `*mut u8` + = note: required for `embassy_sync::lazy_lock::LazyLock<*mut u8>` to implement `Sync` + = note: shared static variables must have a type that implements `Sync` diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.stderr b/embassy-sync/tests/ui/sync_impl/once_lock.stderr new file mode 100644 index 000000000..e2419f844 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/once_lock.stderr @@ -0,0 +1,9 @@ +error[E0277]: `*mut u8` cannot be shared between threads safely + --> tests/ui/sync_impl/once_lock.rs:4:16 + | +4 | static GLOBAL: OnceLock<*mut u8> = OnceLock::new(); + | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `*mut u8` + = note: required for `embassy_sync::once_lock::OnceLock<*mut u8>` to implement `Sync` + = note: shared static variables must have a type that implements `Sync` -- cgit From e19c3a02043077154f034da8c767b3e8e396435f Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Wed, 18 Jun 2025 16:20:50 -0700 Subject: disable ui tests in miri --- embassy-sync/tests/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/tests/ui.rs b/embassy-sync/tests/ui.rs index 9ef0d4c67..e8b1080d8 100644 --- a/embassy-sync/tests/ui.rs +++ b/embassy-sync/tests/ui.rs @@ -1,4 +1,4 @@ -// #[cfg(not(miri))] +#[cfg(not(miri))] #[test] fn ui() { let t = trybuild::TestCases::new(); -- cgit From be5b62bdd469dc7fd68ddb4040b4c0547e65bda8 Mon Sep 17 00:00:00 2001 From: Pietro Lorefice Date: Fri, 20 Jun 2025 10:46:33 +0200 Subject: stm32: hsem: add missing RCC initialization --- embassy-stm32/src/hsem/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 31527bcdb..f648bf861 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -3,7 +3,7 @@ use embassy_hal_internal::PeripheralType; use crate::pac; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. // Those MCUs have a different HSEM implementation (Secure semaphore lock support, // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), @@ -80,6 +80,8 @@ pub struct HardwareSemaphore<'d, T: Instance> { impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// Creates a new HardwareSemaphore instance. pub fn new(peripheral: Peri<'d, T>) -> Self { + rcc::enable_and_reset::(); + HardwareSemaphore { _peri: peripheral } } -- cgit From 59228e2ab4d70eee356400b2470190885527bbc1 Mon Sep 17 00:00:00 2001 From: Pietro Lorefice Date: Fri, 20 Jun 2025 10:47:37 +0200 Subject: stm32: hsem: fix broken CPUID detection --- embassy-stm32/src/hsem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index f648bf861..573a1851d 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -46,7 +46,7 @@ pub enum CoreId { #[inline(always)] pub fn get_current_coreid() -> CoreId { let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; - match cpuid & 0x000000F0 { + match (cpuid & 0x000000F0) >> 4 { #[cfg(any(stm32wb, stm32wl))] 0x0 => CoreId::Core1, -- cgit From 53fd571ddb4774d103340e1442efa671a3564567 Mon Sep 17 00:00:00 2001 From: John Youren <29236922+JYouren@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:45:13 +0100 Subject: Only write to the flash what was read from the file The write method is given the full aligned buffer to write to flash even though it may not be fully populated. This change ensures only what has been read is written to flash. Preventing potential corrupted firmware and additional flash wear. --- examples/boot/application/rp/src/bin/a.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index ede0c07da..e6d7b3d4f 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -54,7 +54,7 @@ async fn main(_s: Spawner) { for chunk in APP_B.chunks(4096) { buf.0[..chunk.len()].copy_from_slice(chunk); defmt::info!("writing block at offset {}", offset); - writer.write(offset, &buf.0[..]).unwrap(); + writer.write(offset, &buf.0[..chunk.len()]).unwrap(); offset += chunk.len() as u32; } watchdog.feed(); -- cgit From f84792aaa7212a38f721ad7f2533f54970f5c298 Mon Sep 17 00:00:00 2001 From: paul fornage Date: Fri, 20 Jun 2025 07:47:30 -0700 Subject: Add syntax highlighter --- docs/index.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.adoc b/docs/index.adoc index 9c6150196..80754d5a4 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -5,6 +5,7 @@ :toc-placement: left :toclevels: 2 :imagesdir: images +:source-highlighter: rouge # Embassy Book -- cgit From b2dcdad51db528e1242ef7ea6028341339a9d488 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sat, 21 Jun 2025 11:58:53 +1000 Subject: BXCAN: Put State inside a critical section mutex of RefCell. This removed unsound code that was giving out mut& to State This change is equiverlent to f5658d6833cb140296a0b6f25b7eb6d16f06c520 that was already done for the FDCAN driver. --- embassy-stm32/src/can/bxcan/mod.rs | 240 +++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 127 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 305666d5b..e2d1c8182 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -35,7 +35,9 @@ impl interrupt::typelevel::Handler for TxInterruptH v.set_rqcp(1, true); v.set_rqcp(2, true); }); - T::state().tx_mode.on_interrupt::(); + T::info().state.lock(|state| { + state.borrow().tx_mode.on_interrupt::(); + }); } } @@ -46,7 +48,9 @@ pub struct Rx0InterruptHandler { impl interrupt::typelevel::Handler for Rx0InterruptHandler { unsafe fn on_interrupt() { - T::state().rx_mode.on_interrupt::(RxFifo::Fifo0); + T::info().state.lock(|state| { + state.borrow().rx_mode.on_interrupt::(RxFifo::Fifo0); + }); } } @@ -57,7 +61,9 @@ pub struct Rx1InterruptHandler { impl interrupt::typelevel::Handler for Rx1InterruptHandler { unsafe fn on_interrupt() { - T::state().rx_mode.on_interrupt::(RxFifo::Fifo1); + T::info().state.lock(|state| { + state.borrow().rx_mode.on_interrupt::(RxFifo::Fifo1); + }); } } @@ -73,7 +79,9 @@ impl interrupt::typelevel::Handler for SceInterrup if msr_val.slaki() { msr.modify(|m| m.set_slaki(true)); - T::state().err_waker.wake(); + T::info().state.lock(|state| { + state.borrow().err_waker.wake(); + }); } else if msr_val.erri() { // Disable the interrupt, but don't acknowledge the error, so that it can be // forwarded off the bus message consumer. If we don't provide some way for @@ -83,7 +91,9 @@ impl interrupt::typelevel::Handler for SceInterrup let ier = T::regs().ier(); ier.modify(|i| i.set_errie(false)); - T::state().err_waker.wake(); + T::info().state.lock(|state| { + state.borrow().err_waker.wake(); + }); } } } @@ -157,7 +167,6 @@ impl Drop for CanConfig<'_> { pub struct Can<'d> { phantom: PhantomData<&'d ()>, info: &'static Info, - state: &'static State, periph_clock: crate::time::Hertz, } @@ -228,7 +237,6 @@ impl<'d> Can<'d> { Self { phantom: PhantomData, info: T::info(), - state: T::state(), periph_clock: T::frequency(), } } @@ -297,7 +305,9 @@ impl<'d> Can<'d> { self.info.regs.0.mcr().modify(|m| m.set_sleep(true)); poll_fn(|cx| { - self.state.err_waker.register(cx.waker()); + self.info.state.lock(|state| { + state.borrow().err_waker.register(cx.waker()); + }); if self.is_sleeping() { Poll::Ready(()) } else { @@ -351,7 +361,6 @@ impl<'d> Can<'d> { CanTx { _phantom: PhantomData, info: self.info, - state: self.state, } .flush_inner(mb) .await; @@ -367,7 +376,6 @@ impl<'d> Can<'d> { CanTx { _phantom: PhantomData, info: self.info, - state: self.state, } .flush_any_inner() .await @@ -378,7 +386,6 @@ impl<'d> Can<'d> { CanTx { _phantom: PhantomData, info: self.info, - state: self.state, } .flush_all_inner() .await @@ -406,19 +413,19 @@ impl<'d> Can<'d> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - self.state.rx_mode.read(self.info, self.state).await + RxMode::read(self.info).await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - self.state.rx_mode.try_read(self.info) + RxMode::try_read(self.info) } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - self.state.rx_mode.wait_not_empty(self.info, self.state).await + RxMode::wait_not_empty(self.info).await } /// Split the CAN driver into transmit and receive halves. @@ -429,12 +436,10 @@ impl<'d> Can<'d> { CanTx { _phantom: PhantomData, info: self.info, - state: self.state, }, CanRx { _phantom: PhantomData, info: self.info, - state: self.state, }, ) } @@ -515,7 +520,6 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_ pub struct CanTx<'d> { _phantom: PhantomData<&'d ()>, info: &'static Info, - state: &'static State, } impl<'d> CanTx<'d> { @@ -524,7 +528,9 @@ impl<'d> CanTx<'d> { /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { poll_fn(|cx| { - self.state.tx_mode.register(cx.waker()); + self.info.state.lock(|state| { + state.borrow().tx_mode.register(cx.waker()); + }); if let Ok(status) = self.info.regs.transmit(frame) { return Poll::Ready(status); } @@ -549,7 +555,9 @@ impl<'d> CanTx<'d> { async fn flush_inner(&self, mb: Mailbox) { poll_fn(|cx| { - self.state.tx_mode.register(cx.waker()); + self.info.state.lock(|state| { + state.borrow().tx_mode.register(cx.waker()); + }); if self.info.regs.0.tsr().read().tme(mb.index()) { return Poll::Ready(()); } @@ -566,7 +574,9 @@ impl<'d> CanTx<'d> { async fn flush_any_inner(&self) { poll_fn(|cx| { - self.state.tx_mode.register(cx.waker()); + self.info.state.lock(|state| { + state.borrow().tx_mode.register(cx.waker()); + }); let tsr = self.info.regs.0.tsr().read(); if tsr.tme(Mailbox::Mailbox0.index()) @@ -593,7 +603,9 @@ impl<'d> CanTx<'d> { async fn flush_all_inner(&self) { poll_fn(|cx| { - self.state.tx_mode.register(cx.waker()); + self.info.state.lock(|state| { + state.borrow().tx_mode.register(cx.waker()); + }); let tsr = self.info.regs.0.tsr().read(); if tsr.tme(Mailbox::Mailbox0.index()) @@ -634,7 +646,7 @@ impl<'d> CanTx<'d> { self, txb: &'static mut TxBuf, ) -> BufferedCanTx<'d, TX_BUF_SIZE> { - BufferedCanTx::new(self.info, self.state, self, txb) + BufferedCanTx::new(self.info, self, txb) } } @@ -644,33 +656,22 @@ pub type TxBuf = Channel { info: &'static Info, - state: &'static State, _tx: CanTx<'d>, tx_buf: &'static TxBuf, } impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { - fn new(info: &'static Info, state: &'static State, _tx: CanTx<'d>, tx_buf: &'static TxBuf) -> Self { - Self { - info, - state, - _tx, - tx_buf, - } - .setup() + fn new(info: &'static Info, _tx: CanTx<'d>, tx_buf: &'static TxBuf) -> Self { + Self { info, _tx, tx_buf }.setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - critical_section::with(|_| { - let tx_inner = super::common::ClassicBufferedTxInner { - tx_receiver: self.tx_buf.receiver().into(), - }; - let state = self.state as *const State; - unsafe { - let mut_state = state as *mut State; - (*mut_state).tx_mode = TxMode::Buffered(tx_inner); - } + let tx_inner = super::common::ClassicBufferedTxInner { + tx_receiver: self.tx_buf.receiver().into(), + }; + self.info.state.lock(|state| { + state.borrow_mut().tx_mode = TxMode::Buffered(tx_inner); }); self } @@ -704,7 +705,6 @@ impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { pub struct CanRx<'d> { _phantom: PhantomData<&'d ()>, info: &'static Info, - state: &'static State, } impl<'d> CanRx<'d> { @@ -714,19 +714,19 @@ impl<'d> CanRx<'d> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - self.state.rx_mode.read(self.info, self.state).await + RxMode::read(self.info).await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - self.state.rx_mode.try_read(self.info) + RxMode::try_read(self.info) } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - self.state.rx_mode.wait_not_empty(self.info, self.state).await + RxMode::wait_not_empty(self.info).await } /// Return a buffered instance of driver. User must supply Buffers @@ -734,7 +734,7 @@ impl<'d> CanRx<'d> { self, rxb: &'static mut RxBuf, ) -> BufferedCanRx<'d, RX_BUF_SIZE> { - BufferedCanRx::new(self.info, self.state, self, rxb) + BufferedCanRx::new(self.info, self, rxb) } /// Accesses the filter banks owned by this CAN peripheral. @@ -752,33 +752,22 @@ pub type RxBuf = Channel { info: &'static Info, - state: &'static State, rx: CanRx<'d>, rx_buf: &'static RxBuf, } impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { - fn new(info: &'static Info, state: &'static State, rx: CanRx<'d>, rx_buf: &'static RxBuf) -> Self { - BufferedCanRx { - info, - state, - rx, - rx_buf, - } - .setup() + fn new(info: &'static Info, rx: CanRx<'d>, rx_buf: &'static RxBuf) -> Self { + BufferedCanRx { info, rx, rx_buf }.setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - critical_section::with(|_| { - let rx_inner = super::common::ClassicBufferedRxInner { - rx_sender: self.rx_buf.sender().into(), - }; - let state = self.state as *const State; - unsafe { - let mut_state = state as *mut State; - (*mut_state).rx_mode = RxMode::Buffered(rx_inner); - } + let rx_inner = super::common::ClassicBufferedRxInner { + rx_sender: self.rx_buf.sender().into(), + }; + self.info.state.lock(|state| { + state.borrow_mut().rx_mode = RxMode::Buffered(rx_inner); }); self } @@ -792,7 +781,7 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - match &self.state.rx_mode { + self.info.state.lock(|s| match &s.borrow().rx_mode { RxMode::Buffered(_) => { if let Ok(result) = self.rx_buf.try_receive() { match result { @@ -810,7 +799,7 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { _ => { panic!("Bad Mode") } - } + }) } /// Waits while receive queue is empty. @@ -929,27 +918,30 @@ impl RxMode { } } - pub(crate) async fn read(&self, info: &Info, state: &State) -> Result { - match self { - Self::NonBuffered(waker) => { - poll_fn(|cx| { - state.err_waker.register(cx.waker()); - waker.register(cx.waker()); - match self.try_read(info) { - Ok(result) => Poll::Ready(Ok(result)), - Err(TryReadError::Empty) => Poll::Pending, - Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), + pub(crate) async fn read(info: &Info) -> Result { + poll_fn(|cx| { + info.state.lock(|state| { + let state = state.borrow(); + state.err_waker.register(cx.waker()); + match &state.rx_mode { + Self::NonBuffered(waker) => { + waker.register(cx.waker()); } - }) - .await - } - _ => { - panic!("Bad Mode") + _ => { + panic!("Bad Mode") + } + } + }); + match RxMode::try_read(info) { + Ok(result) => Poll::Ready(Ok(result)), + Err(TryReadError::Empty) => Poll::Pending, + Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), } - } + }) + .await } - pub(crate) fn try_read(&self, info: &Info) -> Result { - match self { + pub(crate) fn try_read(info: &Info) -> Result { + info.state.lock(|state| match state.borrow().rx_mode { Self::NonBuffered(_) => { let registers = &info.regs; if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { @@ -975,25 +967,28 @@ impl RxMode { _ => { panic!("Bad Mode") } - } + }) } - pub(crate) async fn wait_not_empty(&self, info: &Info, state: &State) { - match &state.rx_mode { - Self::NonBuffered(waker) => { - poll_fn(|cx| { - waker.register(cx.waker()); - if info.regs.receive_frame_available() { - Poll::Ready(()) - } else { - Poll::Pending + pub(crate) async fn wait_not_empty(info: &Info) { + poll_fn(|cx| { + info.state.lock(|s| { + let state = s.borrow(); + match &state.rx_mode { + Self::NonBuffered(waker) => { + waker.register(cx.waker()); } - }) - .await - } - _ => { - panic!("Bad Mode") + _ => { + panic!("Bad Mode") + } + } + }); + if info.regs.receive_frame_available() { + Poll::Ready(()) + } else { + Poll::Pending } - } + }) + .await } } @@ -1008,21 +1003,24 @@ impl TxMode { tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index()) } pub fn on_interrupt(&self) { - match &T::state().tx_mode { - TxMode::NonBuffered(waker) => waker.wake(), - TxMode::Buffered(buf) => { - while self.buffer_free::() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = Registers(T::regs()).transmit(&frame); - } - Err(_) => { - break; + T::info().state.lock(|state| { + let tx_mode = &state.borrow().tx_mode; + match tx_mode { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::Buffered(buf) => { + while self.buffer_free::() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = Registers(T::regs()).transmit(&frame); + } + Err(_) => { + break; + } } } } } - } + }); } fn register(&self, arg: &core::task::Waker) { @@ -1057,6 +1055,7 @@ impl State { } } +type SharedState = embassy_sync::blocking_mutex::Mutex>; pub(crate) struct Info { regs: Registers, tx_interrupt: crate::interrupt::Interrupt, @@ -1065,6 +1064,7 @@ pub(crate) struct Info { sce_interrupt: crate::interrupt::Interrupt, tx_waker: fn(), internal_operation: fn(InternalOperation), + state: SharedState, /// The total number of filter banks available to the instance. /// @@ -1075,8 +1075,6 @@ pub(crate) struct Info { trait SealedInstance { fn info() -> &'static Info; fn regs() -> crate::pac::can::Can; - fn state() -> &'static State; - unsafe fn mut_state() -> &'static mut State; fn internal_operation(val: InternalOperation); } @@ -1136,6 +1134,7 @@ foreach_peripheral!( sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, internal_operation: peripherals::$inst::internal_operation, + state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, }; &INFO @@ -1144,21 +1143,10 @@ foreach_peripheral!( crate::pac::$inst } - unsafe fn mut_state() -> & 'static mut State { - static mut STATE: State = State::new(); - &mut *core::ptr::addr_of_mut!(STATE) - } - fn state() -> &'static State { - unsafe { peripherals::$inst::mut_state() } - } - - fn internal_operation(val: InternalOperation) { - critical_section::with(|_| { - //let state = self.state as *const State; - unsafe { + peripherals::$inst::info().state.lock(|s| { + let mut mut_state = s.borrow_mut(); //let mut_state = state as *mut State; - let mut_state = peripherals::$inst::mut_state(); match val { InternalOperation::NotifySenderCreated => { mut_state.sender_instance_count += 1; @@ -1179,11 +1167,9 @@ foreach_peripheral!( } } } - } }); } } - impl Instance for peripherals::$inst { type TXInterrupt = crate::_generated::peripheral_interrupts::$inst::TX; type RX0Interrupt = crate::_generated::peripheral_interrupts::$inst::RX0; -- cgit From e75b344089df76aa89d7fd4463a509c0bc58de2c Mon Sep 17 00:00:00 2001 From: Tobias Naumann Date: Thu, 5 Jun 2025 15:24:29 +0200 Subject: Add TxGuard and RxGuard which impl RAII for the STM32 CAN reference counting --- embassy-stm32/src/can/common.rs | 53 +++++++++++++++++-------- embassy-stm32/src/can/fdcan.rs | 85 +++++++++++++---------------------------- 2 files changed, 64 insertions(+), 74 deletions(-) diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 386d4467c..651fa12d5 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -25,7 +25,7 @@ pub(crate) struct FdBufferedTxInner { pub struct BufferedSender<'ch, FRAME> { pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>, pub(crate) waker: fn(), - pub(crate) internal_operation: fn(InternalOperation), + pub(crate) tx_guard: TxGuard, } impl<'ch, FRAME> BufferedSender<'ch, FRAME> { @@ -50,28 +50,21 @@ impl<'ch, FRAME> BufferedSender<'ch, FRAME> { impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> { fn clone(&self) -> Self { - (self.internal_operation)(InternalOperation::NotifySenderCreated); Self { tx_buf: self.tx_buf, waker: self.waker, - internal_operation: self.internal_operation, + tx_guard: TxGuard::new(self.tx_guard.internal_operation), } } } -impl<'ch, FRAME> Drop for BufferedSender<'ch, FRAME> { - fn drop(&mut self) { - (self.internal_operation)(InternalOperation::NotifySenderDestroyed); - } -} - /// Sender that can be used for sending Classic CAN frames. pub type BufferedCanSender = BufferedSender<'static, Frame>; /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub struct BufferedReceiver<'ch, ENVELOPE> { pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result>, - pub(crate) internal_operation: fn(InternalOperation), + pub(crate) rx_guard: RxGuard, } impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { @@ -106,19 +99,47 @@ impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { fn clone(&self) -> Self { - (self.internal_operation)(InternalOperation::NotifyReceiverCreated); Self { rx_buf: self.rx_buf, - internal_operation: self.internal_operation, + rx_guard: RxGuard::new(self.rx_guard.internal_operation), } } } -impl<'ch, ENVELOPE> Drop for BufferedReceiver<'ch, ENVELOPE> { +/// A BufferedCanReceiver for Classic CAN frames. +pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; + +/// Implements RAII for the internal reference counting (TX side). Each TX type should contain one +/// of these. The new method and the Drop impl will automatically call the reference counting +/// function. Like this, the reference counting function does not need to be called manually for +/// each TX type. Transceiver types (TX and RX) should contain one TxGuard and one RxGuard. +pub(crate) struct TxGuard { + internal_operation: fn(InternalOperation), +} +impl TxGuard { + pub(crate) fn new(internal_operation: fn(InternalOperation)) -> Self { + internal_operation(InternalOperation::NotifySenderCreated); + Self { internal_operation } + } +} +impl Drop for TxGuard { + fn drop(&mut self) { + (self.internal_operation)(InternalOperation::NotifySenderDestroyed); + } +} + +/// Implements RAII for the internal reference counting (RX side). See TxGuard for further doc. +pub(crate) struct RxGuard { + internal_operation: fn(InternalOperation), +} +impl RxGuard { + pub(crate) fn new(internal_operation: fn(InternalOperation)) -> Self { + internal_operation(InternalOperation::NotifyReceiverCreated); + Self { internal_operation } + } +} +impl Drop for RxGuard { fn drop(&mut self) { (self.internal_operation)(InternalOperation::NotifyReceiverDestroyed); } } - -/// A BufferedCanReceiver for Classic CAN frames. -pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 97d22315a..2846fb44a 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -21,6 +21,7 @@ use self::fd::config::*; use self::fd::filter::*; pub use self::fd::{config, filter}; pub use super::common::{BufferedCanReceiver, BufferedCanSender}; +use super::common::{RxGuard, TxGuard}; use super::enums::*; use super::frame::*; use super::util; @@ -171,6 +172,7 @@ pub struct CanConfigurator<'d> { /// Reference to internals. properties: Properties, periph_clock: crate::time::Hertz, + raii_guards: (TxGuard, RxGuard), } impl<'d> CanConfigurator<'d> { @@ -194,8 +196,6 @@ impl<'d> CanConfigurator<'d> { s.borrow_mut().tx_pin_port = Some(tx.pin_port()); s.borrow_mut().rx_pin_port = Some(rx.pin_port()); }); - (info.internal_operation)(InternalOperation::NotifySenderCreated); - (info.internal_operation)(InternalOperation::NotifyReceiverCreated); let mut config = crate::can::fd::config::FdCanConfig::default(); config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); @@ -214,6 +214,10 @@ impl<'d> CanConfigurator<'d> { info, properties: Properties::new(T::info()), periph_clock: T::frequency(), + raii_guards: ( + TxGuard::new(info.internal_operation), + RxGuard::new(info.internal_operation), + ), } } @@ -267,14 +271,13 @@ impl<'d> CanConfigurator<'d> { s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; }); self.info.regs.into_mode(self.config, mode); - (self.info.internal_operation)(InternalOperation::NotifySenderCreated); - (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); Can { _phantom: PhantomData, config: self.config, info: self.info, _mode: mode, properties: Properties::new(self.info), + raii_guards: self.raii_guards, } } @@ -294,13 +297,6 @@ impl<'d> CanConfigurator<'d> { } } -impl<'d> Drop for CanConfigurator<'d> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); - (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); - } -} - /// FDCAN Instance pub struct Can<'d> { _phantom: PhantomData<&'d ()>, @@ -308,6 +304,7 @@ pub struct Can<'d> { info: &'static Info, _mode: OperatingMode, properties: Properties, + raii_guards: (TxGuard, RxGuard), } impl<'d> Can<'d> { @@ -364,19 +361,19 @@ impl<'d> Can<'d> { /// Split instance into separate portions: Tx(write), Rx(read), common properties pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { - (self.info.internal_operation)(InternalOperation::NotifySenderCreated); - (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); ( CanTx { _phantom: PhantomData, info: self.info, config: self.config, _mode: self._mode, + tx_guard: self.raii_guards.0, }, CanRx { _phantom: PhantomData, info: self.info, _mode: self._mode, + rx_guard: self.raii_guards.1, }, Properties { info: self.properties.info, @@ -385,14 +382,13 @@ impl<'d> Can<'d> { } /// Join split rx and tx portions back together pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { - (tx.info.internal_operation)(InternalOperation::NotifySenderCreated); - (tx.info.internal_operation)(InternalOperation::NotifyReceiverCreated); Can { _phantom: PhantomData, config: tx.config, info: tx.info, _mode: rx._mode, properties: Properties::new(tx.info), + raii_guards: (tx.tx_guard, rx.rx_guard), } } @@ -415,13 +411,6 @@ impl<'d> Can<'d> { } } -impl<'d> Drop for Can<'d> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); - (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); - } -} - /// User supplied buffer for RX Buffering pub type RxBuf = Channel, BUF_SIZE>; @@ -436,6 +425,7 @@ pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, properties: Properties, + _raii_guards: (TxGuard, RxGuard), } impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { @@ -445,8 +435,6 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, ) -> Self { - (info.internal_operation)(InternalOperation::NotifySenderCreated); - (info.internal_operation)(InternalOperation::NotifyReceiverCreated); BufferedCan { _phantom: PhantomData, info, @@ -454,6 +442,10 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, tx_buf, rx_buf, properties: Properties::new(info), + _raii_guards: ( + TxGuard::new(info.internal_operation), + RxGuard::new(info.internal_operation), + ), } .setup() } @@ -492,31 +484,22 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, /// Returns a sender that can be used for sending CAN frames. pub fn writer(&self) -> BufferedCanSender { - (self.info.internal_operation)(InternalOperation::NotifySenderCreated); BufferedCanSender { tx_buf: self.tx_buf.sender().into(), waker: self.info.tx_waker, - internal_operation: self.info.internal_operation, + tx_guard: TxGuard::new(self.info.internal_operation), } } /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub fn reader(&self) -> BufferedCanReceiver { - (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); BufferedCanReceiver { rx_buf: self.rx_buf.receiver().into(), - internal_operation: self.info.internal_operation, + rx_guard: RxGuard::new(self.info.internal_operation), } } } -impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); - (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); - } -} - /// User supplied buffer for RX Buffering pub type RxFdBuf = Channel, BUF_SIZE>; @@ -537,6 +520,7 @@ pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, properties: Properties, + _raii_guards: (TxGuard, RxGuard), } impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { @@ -546,8 +530,6 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, ) -> Self { - (info.internal_operation)(InternalOperation::NotifySenderCreated); - (info.internal_operation)(InternalOperation::NotifyReceiverCreated); BufferedCanFd { _phantom: PhantomData, info, @@ -555,6 +537,10 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' tx_buf, rx_buf, properties: Properties::new(info), + _raii_guards: ( + TxGuard::new(info.internal_operation), + RxGuard::new(info.internal_operation), + ), } .setup() } @@ -597,7 +583,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' BufferedFdCanSender { tx_buf: self.tx_buf.sender().into(), waker: self.info.tx_waker, - internal_operation: self.info.internal_operation, + tx_guard: TxGuard::new(self.info.internal_operation), } } @@ -606,23 +592,17 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); BufferedFdCanReceiver { rx_buf: self.rx_buf.receiver().into(), - internal_operation: self.info.internal_operation, + rx_guard: RxGuard::new(self.info.internal_operation), } } } -impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); - (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); - } -} - /// FDCAN Rx only Instance pub struct CanRx<'d> { _phantom: PhantomData<&'d ()>, info: &'static Info, _mode: OperatingMode, + rx_guard: RxGuard, } impl<'d> CanRx<'d> { @@ -637,18 +617,13 @@ impl<'d> CanRx<'d> { } } -impl<'d> Drop for CanRx<'d> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); - } -} - /// FDCAN Tx only Instance pub struct CanTx<'d> { _phantom: PhantomData<&'d ()>, info: &'static Info, config: crate::can::fd::config::FdCanConfig, _mode: OperatingMode, + tx_guard: TxGuard, } impl<'c, 'd> CanTx<'d> { @@ -669,12 +644,6 @@ impl<'c, 'd> CanTx<'d> { } } -impl<'d> Drop for CanTx<'d> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); - } -} - enum RxMode { NonBuffered(AtomicWaker), ClassicBuffered(super::common::ClassicBufferedRxInner), -- cgit From 8b280688e18bd92b126601d9af26d73e147edeac Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sat, 7 Jun 2025 17:25:15 +1000 Subject: FDCAN/BXCAN: Finish implementation of RAII instance counters. - Use DeRef in all types - Change Name of internal_operation and its enum - move into Info to avoid macro code dup --- embassy-stm32/src/can/bxcan/mod.rs | 191 ++++++++++++++++++------------------- embassy-stm32/src/can/common.rs | 108 +++++++++++++++------ embassy-stm32/src/can/enums.rs | 2 +- embassy-stm32/src/can/fdcan.rs | 163 +++++++++++++------------------ 4 files changed, 243 insertions(+), 221 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index e2d1c8182..4c0795a2a 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -15,9 +15,10 @@ pub use embedded_can::{ExtendedId, Id, StandardId}; use self::filter::MasterFilters; use self::registers::{Registers, RxFifo}; pub use super::common::{BufferedCanReceiver, BufferedCanSender}; +use super::common::{InfoRef, RxInfoRef, TxInfoRef}; use super::frame::{Envelope, Frame}; use super::util; -use crate::can::enums::{BusError, InternalOperation, TryReadError}; +use crate::can::enums::{BusError, RefCountOp, TryReadError}; use crate::gpio::{AfType, OutputType, Pull, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::rcc::{self, RccPeripheral}; @@ -90,7 +91,6 @@ impl interrupt::typelevel::Handler for SceInterrup // an indefinite amount of time. let ier = T::regs().ier(); ier.modify(|i| i.set_errie(false)); - T::info().state.lock(|state| { state.borrow().err_waker.wake(); }); @@ -101,7 +101,7 @@ impl interrupt::typelevel::Handler for SceInterrup /// Configuration proxy returned by [`Can::modify_config`]. pub struct CanConfig<'a> { phantom: PhantomData<&'a ()>, - info: &'static Info, + info: InfoRef, periph_clock: crate::time::Hertz, } @@ -166,7 +166,7 @@ impl Drop for CanConfig<'_> { /// CAN driver pub struct Can<'d> { phantom: PhantomData<&'d ()>, - info: &'static Info, + info: InfoRef, periph_clock: crate::time::Hertz, } @@ -236,7 +236,7 @@ impl<'d> Can<'d> { Self { phantom: PhantomData, - info: T::info(), + info: InfoRef::new(T::info()), periph_clock: T::frequency(), } } @@ -256,7 +256,7 @@ impl<'d> Can<'d> { CanConfig { phantom: self.phantom, - info: self.info, + info: InfoRef::new(&self.info), periph_clock: self.periph_clock, } } @@ -305,8 +305,8 @@ impl<'d> Can<'d> { self.info.regs.0.mcr().modify(|m| m.set_sleep(true)); poll_fn(|cx| { - self.info.state.lock(|state| { - state.borrow().err_waker.register(cx.waker()); + self.info.state.lock(|s| { + s.borrow().err_waker.register(cx.waker()); }); if self.is_sleeping() { Poll::Ready(()) @@ -360,7 +360,7 @@ impl<'d> Can<'d> { pub async fn flush(&self, mb: Mailbox) { CanTx { _phantom: PhantomData, - info: self.info, + info: TxInfoRef::new(&self.info), } .flush_inner(mb) .await; @@ -375,7 +375,7 @@ impl<'d> Can<'d> { pub async fn flush_any(&self) { CanTx { _phantom: PhantomData, - info: self.info, + info: TxInfoRef::new(&self.info), } .flush_any_inner() .await @@ -385,7 +385,7 @@ impl<'d> Can<'d> { pub async fn flush_all(&self) { CanTx { _phantom: PhantomData, - info: self.info, + info: TxInfoRef::new(&self.info), } .flush_all_inner() .await @@ -413,19 +413,19 @@ impl<'d> Can<'d> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - RxMode::read(self.info).await + RxMode::read(&self.info).await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - RxMode::try_read(self.info) + RxMode::try_read(&self.info) } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - RxMode::wait_not_empty(self.info).await + RxMode::wait_not_empty(&self.info).await } /// Split the CAN driver into transmit and receive halves. @@ -435,11 +435,11 @@ impl<'d> Can<'d> { ( CanTx { _phantom: PhantomData, - info: self.info, + info: TxInfoRef::new(&self.info), }, CanRx { _phantom: PhantomData, - info: self.info, + info: RxInfoRef::new(&self.info), }, ) } @@ -464,7 +464,7 @@ impl<'d> Can<'d> { /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master /// peripheral instead. pub fn modify_filters(&mut self) -> MasterFilters<'_> { - unsafe { MasterFilters::new(self.info) } + unsafe { MasterFilters::new(&self.info) } } } @@ -519,7 +519,7 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_ /// CAN driver, transmit half. pub struct CanTx<'d> { _phantom: PhantomData<&'d ()>, - info: &'static Info, + info: TxInfoRef, } impl<'d> CanTx<'d> { @@ -528,8 +528,8 @@ impl<'d> CanTx<'d> { /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { poll_fn(|cx| { - self.info.state.lock(|state| { - state.borrow().tx_mode.register(cx.waker()); + self.info.state.lock(|s| { + s.borrow().tx_mode.register(cx.waker()); }); if let Ok(status) = self.info.regs.transmit(frame) { return Poll::Ready(status); @@ -555,8 +555,8 @@ impl<'d> CanTx<'d> { async fn flush_inner(&self, mb: Mailbox) { poll_fn(|cx| { - self.info.state.lock(|state| { - state.borrow().tx_mode.register(cx.waker()); + self.info.state.lock(|s| { + s.borrow().tx_mode.register(cx.waker()); }); if self.info.regs.0.tsr().read().tme(mb.index()) { return Poll::Ready(()); @@ -574,8 +574,8 @@ impl<'d> CanTx<'d> { async fn flush_any_inner(&self) { poll_fn(|cx| { - self.info.state.lock(|state| { - state.borrow().tx_mode.register(cx.waker()); + self.info.state.lock(|s| { + s.borrow().tx_mode.register(cx.waker()); }); let tsr = self.info.regs.0.tsr().read(); @@ -603,8 +603,8 @@ impl<'d> CanTx<'d> { async fn flush_all_inner(&self) { poll_fn(|cx| { - self.info.state.lock(|state| { - state.borrow().tx_mode.register(cx.waker()); + self.info.state.lock(|s| { + s.borrow().tx_mode.register(cx.waker()); }); let tsr = self.info.regs.0.tsr().read(); @@ -646,7 +646,7 @@ impl<'d> CanTx<'d> { self, txb: &'static mut TxBuf, ) -> BufferedCanTx<'d, TX_BUF_SIZE> { - BufferedCanTx::new(self.info, self, txb) + BufferedCanTx::new(&self.info, self, txb) } } @@ -655,23 +655,30 @@ pub type TxBuf = Channel { - info: &'static Info, + info: TxInfoRef, _tx: CanTx<'d>, tx_buf: &'static TxBuf, } impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { fn new(info: &'static Info, _tx: CanTx<'d>, tx_buf: &'static TxBuf) -> Self { - Self { info, _tx, tx_buf }.setup() + Self { + info: TxInfoRef::new(info), + _tx, + tx_buf, + } + .setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - let tx_inner = super::common::ClassicBufferedTxInner { - tx_receiver: self.tx_buf.receiver().into(), - }; - self.info.state.lock(|state| { - state.borrow_mut().tx_mode = TxMode::Buffered(tx_inner); + critical_section::with(|_| { + let tx_inner = super::common::ClassicBufferedTxInner { + tx_receiver: self.tx_buf.receiver().into(), + }; + self.info.state.lock(|s| { + s.borrow_mut().tx_mode = TxMode::Buffered(tx_inner); + }); }); self } @@ -685,26 +692,18 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { /// Returns a sender that can be used for sending CAN frames. pub fn writer(&self) -> BufferedCanSender { - (self.info.internal_operation)(InternalOperation::NotifySenderCreated); BufferedCanSender { tx_buf: self.tx_buf.sender().into(), - waker: self.info.tx_waker, - internal_operation: self.info.internal_operation, + info: TxInfoRef::new(&self.info), } } } -impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); - } -} - /// CAN driver, receive half. #[allow(dead_code)] pub struct CanRx<'d> { _phantom: PhantomData<&'d ()>, - info: &'static Info, + info: RxInfoRef, } impl<'d> CanRx<'d> { @@ -714,19 +713,19 @@ impl<'d> CanRx<'d> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - RxMode::read(self.info).await + RxMode::read(&self.info).await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - RxMode::try_read(self.info) + RxMode::try_read(&self.info) } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - RxMode::wait_not_empty(self.info).await + RxMode::wait_not_empty(&self.info).await } /// Return a buffered instance of driver. User must supply Buffers @@ -734,7 +733,7 @@ impl<'d> CanRx<'d> { self, rxb: &'static mut RxBuf, ) -> BufferedCanRx<'d, RX_BUF_SIZE> { - BufferedCanRx::new(self.info, self, rxb) + BufferedCanRx::new(&self.info, self, rxb) } /// Accesses the filter banks owned by this CAN peripheral. @@ -742,7 +741,7 @@ impl<'d> CanRx<'d> { /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master /// peripheral instead. pub fn modify_filters(&mut self) -> MasterFilters<'_> { - unsafe { MasterFilters::new(self.info) } + unsafe { MasterFilters::new(&self.info) } } } @@ -751,23 +750,30 @@ pub type RxBuf = Channel { - info: &'static Info, + info: RxInfoRef, rx: CanRx<'d>, rx_buf: &'static RxBuf, } impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { fn new(info: &'static Info, rx: CanRx<'d>, rx_buf: &'static RxBuf) -> Self { - BufferedCanRx { info, rx, rx_buf }.setup() + BufferedCanRx { + info: RxInfoRef::new(info), + rx, + rx_buf, + } + .setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - let rx_inner = super::common::ClassicBufferedRxInner { - rx_sender: self.rx_buf.sender().into(), - }; - self.info.state.lock(|state| { - state.borrow_mut().rx_mode = RxMode::Buffered(rx_inner); + critical_section::with(|_| { + let rx_inner = super::common::ClassicBufferedRxInner { + rx_sender: self.rx_buf.sender().into(), + }; + self.info.state.lock(|s| { + s.borrow_mut().rx_mode = RxMode::Buffered(rx_inner); + }); }); self } @@ -809,10 +815,9 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub fn reader(&self) -> BufferedCanReceiver { - (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); BufferedCanReceiver { rx_buf: self.rx_buf.receiver().into(), - internal_operation: self.info.internal_operation, + info: RxInfoRef::new(&self.info), } } @@ -825,12 +830,6 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { } } -impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> { - fn drop(&mut self) { - (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); - } -} - impl Drop for Can<'_> { fn drop(&mut self) { // Cannot call `free()` because it moves the instance. @@ -1005,6 +1004,7 @@ impl TxMode { pub fn on_interrupt(&self) { T::info().state.lock(|state| { let tx_mode = &state.borrow().tx_mode; + match tx_mode { TxMode::NonBuffered(waker) => waker.wake(), TxMode::Buffered(buf) => { @@ -1062,8 +1062,7 @@ pub(crate) struct Info { rx0_interrupt: crate::interrupt::Interrupt, rx1_interrupt: crate::interrupt::Interrupt, sce_interrupt: crate::interrupt::Interrupt, - tx_waker: fn(), - internal_operation: fn(InternalOperation), + pub(crate) tx_waker: fn(), state: SharedState, /// The total number of filter banks available to the instance. @@ -1072,10 +1071,37 @@ pub(crate) struct Info { num_filter_banks: u8, } +impl Info { + pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) { + self.state.lock(|s| { + let mut mut_state = s.borrow_mut(); + match val { + RefCountOp::NotifySenderCreated => { + mut_state.sender_instance_count += 1; + } + RefCountOp::NotifySenderDestroyed => { + mut_state.sender_instance_count -= 1; + if 0 == mut_state.sender_instance_count { + (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } + } + RefCountOp::NotifyReceiverCreated => { + mut_state.receiver_instance_count += 1; + } + RefCountOp::NotifyReceiverDestroyed => { + mut_state.receiver_instance_count -= 1; + if 0 == mut_state.receiver_instance_count { + (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } + } + } + }); + } +} + trait SealedInstance { fn info() -> &'static Info; fn regs() -> crate::pac::can::Can; - fn internal_operation(val: InternalOperation); } /// CAN instance trait. @@ -1133,43 +1159,16 @@ foreach_peripheral!( rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, - internal_operation: peripherals::$inst::internal_operation, - state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, + state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), }; &INFO } fn regs() -> crate::pac::can::Can { crate::pac::$inst } - - fn internal_operation(val: InternalOperation) { - peripherals::$inst::info().state.lock(|s| { - let mut mut_state = s.borrow_mut(); - //let mut_state = state as *mut State; - match val { - InternalOperation::NotifySenderCreated => { - mut_state.sender_instance_count += 1; - } - InternalOperation::NotifySenderDestroyed => { - mut_state.sender_instance_count -= 1; - if ( 0 == mut_state.sender_instance_count) { - (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - } - } - InternalOperation::NotifyReceiverCreated => { - mut_state.receiver_instance_count += 1; - } - InternalOperation::NotifyReceiverDestroyed => { - mut_state.receiver_instance_count -= 1; - if ( 0 == mut_state.receiver_instance_count) { - (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - } - } - } - }); - } } + impl Instance for peripherals::$inst { type TXInterrupt = crate::_generated::peripheral_interrupts::$inst::TX; type RX0Interrupt = crate::_generated::peripheral_interrupts::$inst::RX0; diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 651fa12d5..980f33a04 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -24,22 +24,21 @@ pub(crate) struct FdBufferedTxInner { /// Sender that can be used for sending CAN frames. pub struct BufferedSender<'ch, FRAME> { pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>, - pub(crate) waker: fn(), - pub(crate) tx_guard: TxGuard, + pub(crate) info: TxInfoRef, } impl<'ch, FRAME> BufferedSender<'ch, FRAME> { /// Async write frame to TX buffer. pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError> { self.tx_buf.try_send(frame)?; - (self.waker)(); + (self.info.tx_waker)(); Ok(()) } /// Async write frame to TX buffer. pub async fn write(&mut self, frame: FRAME) { self.tx_buf.send(frame).await; - (self.waker)(); + (self.info.tx_waker)(); } /// Allows a poll_fn to poll until the channel is ready to write @@ -52,8 +51,7 @@ impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> { fn clone(&self) -> Self { Self { tx_buf: self.tx_buf, - waker: self.waker, - tx_guard: TxGuard::new(self.tx_guard.internal_operation), + info: TxInfoRef::new(&self.info), } } } @@ -64,7 +62,7 @@ pub type BufferedCanSender = BufferedSender<'static, Frame>; /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub struct BufferedReceiver<'ch, ENVELOPE> { pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result>, - pub(crate) rx_guard: RxGuard, + pub(crate) info: RxInfoRef, } impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { @@ -101,7 +99,7 @@ impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { fn clone(&self) -> Self { Self { rx_buf: self.rx_buf, - rx_guard: RxGuard::new(self.rx_guard.internal_operation), + info: RxInfoRef::new(&self.info), } } } @@ -109,37 +107,89 @@ impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { /// A BufferedCanReceiver for Classic CAN frames. pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; -/// Implements RAII for the internal reference counting (TX side). Each TX type should contain one -/// of these. The new method and the Drop impl will automatically call the reference counting -/// function. Like this, the reference counting function does not need to be called manually for -/// each TX type. Transceiver types (TX and RX) should contain one TxGuard and one RxGuard. -pub(crate) struct TxGuard { - internal_operation: fn(InternalOperation), +/// Provides a reference to the driver internals and implements RAII for the internal reference +/// counting. Each type that can operate on the driver should contain either InfoRef +/// or the similar TxInfoRef or RxInfoRef. The new method and the Drop impl will automatically +/// call the reference counting function. Like this, the reference counting function does not +/// need to be called manually for each type. +pub(crate) struct InfoRef { + info: &'static super::Info, +} +impl InfoRef { + pub(crate) fn new(info: &'static super::Info) -> Self { + info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated); + info.adjust_reference_counter(RefCountOp::NotifySenderCreated); + Self { info } + } +} + +impl Drop for InfoRef { + fn drop(&mut self) { + self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed); + self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed); + } +} + +impl core::ops::Deref for InfoRef { + type Target = &'static super::Info; + + fn deref(&self) -> &Self::Target { + &self.info + } } -impl TxGuard { - pub(crate) fn new(internal_operation: fn(InternalOperation)) -> Self { - internal_operation(InternalOperation::NotifySenderCreated); - Self { internal_operation } + +/// Provides a reference to the driver internals and implements RAII for the internal reference +/// counting for Tx only types. +/// See InfoRef for further doc. +pub(crate) struct TxInfoRef { + info: &'static super::Info, +} + +impl TxInfoRef { + pub(crate) fn new(info: &'static super::Info) -> Self { + info.adjust_reference_counter(RefCountOp::NotifySenderCreated); + Self { info } } } -impl Drop for TxGuard { + +impl Drop for TxInfoRef { fn drop(&mut self) { - (self.internal_operation)(InternalOperation::NotifySenderDestroyed); + self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed); + } +} + +impl core::ops::Deref for TxInfoRef { + type Target = &'static super::Info; + + fn deref(&self) -> &Self::Target { + &self.info } } -/// Implements RAII for the internal reference counting (RX side). See TxGuard for further doc. -pub(crate) struct RxGuard { - internal_operation: fn(InternalOperation), +/// Provides a reference to the driver internals and implements RAII for the internal reference +/// counting for Rx only types. +/// See InfoRef for further doc. +pub(crate) struct RxInfoRef { + info: &'static super::Info, } -impl RxGuard { - pub(crate) fn new(internal_operation: fn(InternalOperation)) -> Self { - internal_operation(InternalOperation::NotifyReceiverCreated); - Self { internal_operation } + +impl RxInfoRef { + pub(crate) fn new(info: &'static super::Info) -> Self { + info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated); + Self { info } } } -impl Drop for RxGuard { + +impl Drop for RxInfoRef { fn drop(&mut self) { - (self.internal_operation)(InternalOperation::NotifyReceiverDestroyed); + self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed); + } +} + +impl core::ops::Deref for RxInfoRef { + type Target = &'static super::Info; + + fn deref(&self) -> &Self::Target { + &self.info } } diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index 97cb47640..6d91020fc 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs @@ -72,7 +72,7 @@ pub enum TryReadError { /// Internal Operation #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InternalOperation { +pub enum RefCountOp { /// Notify receiver created NotifyReceiverCreated, /// Notify receiver destroyed diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 2846fb44a..99e40ba62 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -21,7 +21,7 @@ use self::fd::config::*; use self::fd::filter::*; pub use self::fd::{config, filter}; pub use super::common::{BufferedCanReceiver, BufferedCanSender}; -use super::common::{RxGuard, TxGuard}; +use super::common::{InfoRef, RxInfoRef, TxInfoRef}; use super::enums::*; use super::frame::*; use super::util; @@ -168,11 +168,10 @@ fn calc_ns_per_timer_tick( pub struct CanConfigurator<'d> { _phantom: PhantomData<&'d ()>, config: crate::can::fd::config::FdCanConfig, - info: &'static Info, /// Reference to internals. properties: Properties, periph_clock: crate::time::Hertz, - raii_guards: (TxGuard, RxGuard), + info: InfoRef, } impl<'d> CanConfigurator<'d> { @@ -211,13 +210,9 @@ impl<'d> CanConfigurator<'d> { Self { _phantom: PhantomData, config, - info, properties: Properties::new(T::info()), periph_clock: T::frequency(), - raii_guards: ( - TxGuard::new(info.internal_operation), - RxGuard::new(info.internal_operation), - ), + info: InfoRef::new(info), } } @@ -266,7 +261,7 @@ impl<'d> CanConfigurator<'d> { /// Start in mode. pub fn start(self, mode: OperatingMode) -> Can<'d> { - let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); + let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); self.info.state.lock(|s| { s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; }); @@ -274,10 +269,9 @@ impl<'d> CanConfigurator<'d> { Can { _phantom: PhantomData, config: self.config, - info: self.info, _mode: mode, - properties: Properties::new(self.info), - raii_guards: self.raii_guards, + properties: Properties::new(&self.info), + info: InfoRef::new(&self.info), } } @@ -301,10 +295,9 @@ impl<'d> CanConfigurator<'d> { pub struct Can<'d> { _phantom: PhantomData<&'d ()>, config: crate::can::fd::config::FdCanConfig, - info: &'static Info, _mode: OperatingMode, properties: Properties, - raii_guards: (TxGuard, RxGuard), + info: InfoRef, } impl<'d> Can<'d> { @@ -338,12 +331,12 @@ impl<'d> Can<'d> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write(&mut self, frame: &Frame) -> Option { - TxMode::write(self.info, frame).await + TxMode::write(&self.info, frame).await } /// Returns the next received message frame pub async fn read(&mut self) -> Result { - RxMode::read_classic(self.info).await + RxMode::read_classic(&self.info).await } /// Queues the message to be sent but exerts backpressure. If a lower-priority @@ -351,12 +344,12 @@ impl<'d> Can<'d> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write_fd(&mut self, frame: &FdFrame) -> Option { - TxMode::write_fd(self.info, frame).await + TxMode::write_fd(&self.info, frame).await } /// Returns the next received message frame pub async fn read_fd(&mut self) -> Result { - RxMode::read_fd(self.info).await + RxMode::read_fd(&self.info).await } /// Split instance into separate portions: Tx(write), Rx(read), common properties @@ -364,16 +357,14 @@ impl<'d> Can<'d> { ( CanTx { _phantom: PhantomData, - info: self.info, config: self.config, _mode: self._mode, - tx_guard: self.raii_guards.0, + info: TxInfoRef::new(&self.info), }, CanRx { _phantom: PhantomData, - info: self.info, _mode: self._mode, - rx_guard: self.raii_guards.1, + info: RxInfoRef::new(&self.info), }, Properties { info: self.properties.info, @@ -385,10 +376,9 @@ impl<'d> Can<'d> { Can { _phantom: PhantomData, config: tx.config, - info: tx.info, _mode: rx._mode, - properties: Properties::new(tx.info), - raii_guards: (tx.tx_guard, rx.rx_guard), + properties: Properties::new(&tx.info), + info: InfoRef::new(&tx.info), } } @@ -398,7 +388,7 @@ impl<'d> Can<'d> { tx_buf: &'static mut TxBuf, rxb: &'static mut RxBuf, ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { - BufferedCan::new(self.info, self._mode, tx_buf, rxb) + BufferedCan::new(&self.info, self._mode, tx_buf, rxb) } /// Return a buffered instance of driver with CAN FD support. User must supply Buffers @@ -407,7 +397,7 @@ impl<'d> Can<'d> { tx_buf: &'static mut TxFdBuf, rxb: &'static mut RxFdBuf, ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { - BufferedCanFd::new(self.info, self._mode, tx_buf, rxb) + BufferedCanFd::new(&self.info, self._mode, tx_buf, rxb) } } @@ -420,12 +410,11 @@ pub type TxBuf = Channel { _phantom: PhantomData<&'d ()>, - info: &'static Info, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, properties: Properties, - _raii_guards: (TxGuard, RxGuard), + info: InfoRef, } impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { @@ -437,15 +426,11 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, ) -> Self { BufferedCan { _phantom: PhantomData, - info, _mode, tx_buf, rx_buf, properties: Properties::new(info), - _raii_guards: ( - TxGuard::new(info.internal_operation), - RxGuard::new(info.internal_operation), - ), + info: InfoRef::new(info), } .setup() } @@ -486,8 +471,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, pub fn writer(&self) -> BufferedCanSender { BufferedCanSender { tx_buf: self.tx_buf.sender().into(), - waker: self.info.tx_waker, - tx_guard: TxGuard::new(self.info.internal_operation), + info: TxInfoRef::new(&self.info), } } @@ -495,7 +479,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, pub fn reader(&self) -> BufferedCanReceiver { BufferedCanReceiver { rx_buf: self.rx_buf.receiver().into(), - rx_guard: RxGuard::new(self.info.internal_operation), + info: RxInfoRef::new(&self.info), } } } @@ -515,12 +499,11 @@ pub type BufferedFdCanReceiver = super::common::BufferedReceiver<'static, FdEnve /// Buffered FDCAN Instance pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { _phantom: PhantomData<&'d ()>, - info: &'static Info, _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, properties: Properties, - _raii_guards: (TxGuard, RxGuard), + info: InfoRef, } impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { @@ -532,15 +515,11 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' ) -> Self { BufferedCanFd { _phantom: PhantomData, - info, _mode, tx_buf, rx_buf, properties: Properties::new(info), - _raii_guards: ( - TxGuard::new(info.internal_operation), - RxGuard::new(info.internal_operation), - ), + info: InfoRef::new(info), } .setup() } @@ -579,20 +558,17 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' /// Returns a sender that can be used for sending CAN frames. pub fn writer(&self) -> BufferedFdCanSender { - (self.info.internal_operation)(InternalOperation::NotifySenderCreated); BufferedFdCanSender { tx_buf: self.tx_buf.sender().into(), - waker: self.info.tx_waker, - tx_guard: TxGuard::new(self.info.internal_operation), + info: TxInfoRef::new(&self.info), } } /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub fn reader(&self) -> BufferedFdCanReceiver { - (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); BufferedFdCanReceiver { rx_buf: self.rx_buf.receiver().into(), - rx_guard: RxGuard::new(self.info.internal_operation), + info: RxInfoRef::new(&self.info), } } } @@ -600,9 +576,8 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' /// FDCAN Rx only Instance pub struct CanRx<'d> { _phantom: PhantomData<&'d ()>, - info: &'static Info, _mode: OperatingMode, - rx_guard: RxGuard, + info: RxInfoRef, } impl<'d> CanRx<'d> { @@ -620,10 +595,9 @@ impl<'d> CanRx<'d> { /// FDCAN Tx only Instance pub struct CanTx<'d> { _phantom: PhantomData<&'d ()>, - info: &'static Info, config: crate::can::fd::config::FdCanConfig, _mode: OperatingMode, - tx_guard: TxGuard, + info: TxInfoRef, } impl<'c, 'd> CanTx<'d> { @@ -632,7 +606,7 @@ impl<'c, 'd> CanTx<'d> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write(&mut self, frame: &Frame) -> Option { - TxMode::write(self.info, frame).await + TxMode::write(&self.info, frame).await } /// Queues the message to be sent but exerts backpressure. If a lower-priority @@ -640,7 +614,7 @@ impl<'c, 'd> CanTx<'d> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write_fd(&mut self, frame: &FdFrame) -> Option { - TxMode::write_fd(self.info, frame).await + TxMode::write_fd(&self.info, frame).await } } @@ -907,21 +881,56 @@ impl State { } type SharedState = embassy_sync::blocking_mutex::Mutex>; -struct Info { +pub(crate) struct Info { regs: Registers, interrupt0: crate::interrupt::Interrupt, _interrupt1: crate::interrupt::Interrupt, - tx_waker: fn(), - internal_operation: fn(InternalOperation), + pub(crate) tx_waker: fn(), state: SharedState, } +impl Info { + pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) { + self.state.lock(|s| { + let mut mut_state = s.borrow_mut(); + match val { + RefCountOp::NotifySenderCreated => { + mut_state.sender_instance_count += 1; + } + RefCountOp::NotifySenderDestroyed => { + mut_state.sender_instance_count -= 1; + if 0 == mut_state.sender_instance_count { + (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } + } + RefCountOp::NotifyReceiverCreated => { + mut_state.receiver_instance_count += 1; + } + RefCountOp::NotifyReceiverDestroyed => { + mut_state.receiver_instance_count -= 1; + if 0 == mut_state.receiver_instance_count { + (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } + } + } + if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 { + unsafe { + let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap()); + tx_pin.set_as_disconnected(); + let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap()); + rx_pin.set_as_disconnected(); + self.interrupt0.disable(); + } + } + }); + } +} + trait SealedInstance { const MSG_RAM_OFFSET: usize; fn info() -> &'static Info; fn registers() -> crate::can::fd::peripheral::Registers; - fn internal_operation(val: InternalOperation); } /// Instance trait @@ -943,41 +952,6 @@ macro_rules! impl_fdcan { impl SealedInstance for peripherals::$inst { const MSG_RAM_OFFSET: usize = $msg_ram_offset; - fn internal_operation(val: InternalOperation) { - peripherals::$inst::info().state.lock(|s| { - let mut mut_state = s.borrow_mut(); - match val { - InternalOperation::NotifySenderCreated => { - mut_state.sender_instance_count += 1; - } - InternalOperation::NotifySenderDestroyed => { - mut_state.sender_instance_count -= 1; - if ( 0 == mut_state.sender_instance_count) { - (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - } - } - InternalOperation::NotifyReceiverCreated => { - mut_state.receiver_instance_count += 1; - } - InternalOperation::NotifyReceiverDestroyed => { - mut_state.receiver_instance_count -= 1; - if ( 0 == mut_state.receiver_instance_count) { - (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - } - } - } - if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 { - unsafe { - let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap()); - tx_pin.set_as_disconnected(); - let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap()); - rx_pin.set_as_disconnected(); - rcc::disable::(); - } - } - }); - } - fn info() -> &'static Info { static INFO: Info = Info { @@ -985,7 +959,6 @@ macro_rules! impl_fdcan { interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, - internal_operation: peripherals::$inst::internal_operation, state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), }; &INFO -- cgit From 9cb5c6d001bff9615417d8b0999c9cd33fff79d6 Mon Sep 17 00:00:00 2001 From: jake-taf <149392739+jake-taf@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:30:09 -0400 Subject: Fix issues #4333 Allow configs to be used in this macro --- embassy-stm32/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 973acc9bb..06c91ef97 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -178,7 +178,7 @@ pub use crate::_generated::interrupt; macro_rules! bind_interrupts { ($(#[$outer:meta])* $vis:vis struct $name:ident { $( - $(#[$inner:meta])* + $(#[doc = $doc:literal])* $(#[cfg($cond_irq:meta)])? $irq:ident => $( $(#[cfg($cond_handler:meta)])? @@ -194,7 +194,7 @@ macro_rules! bind_interrupts { #[allow(non_snake_case)] #[no_mangle] $(#[cfg($cond_irq)])? - $(#[$inner])* + $(#[doc = $doc])* unsafe extern "C" fn $irq() { $( $(#[cfg($cond_handler)])? -- cgit From 5da6e31a3e4a1175ddf34e1ebdf83a8d685ea521 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Tue, 24 Jun 2025 16:19:33 +0200 Subject: Correct esp-hal link --- docs/pages/overview.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/overview.adoc b/docs/pages/overview.adoc index acd757795..18eaaeb75 100644 --- a/docs/pages/overview.adoc +++ b/docs/pages/overview.adoc @@ -30,7 +30,7 @@ The Embassy project maintains HALs for select hardware, but you can still use HA * link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series. * link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller. * link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers. -* link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips. +* link:https://github.com/esp-rs/esp-hal[esp-hal], for the Espressif Systems ESP32 series of chips. * link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips. * link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC. * link:https://github.com/py32-rs/py32-hal[py32-hal], for the Puya Semiconductor PY32 series of chips. -- cgit From cbf61765f166edd7377e148291c102c61903efe8 Mon Sep 17 00:00:00 2001 From: Thomas Giesel Date: Mon, 9 Jun 2025 21:15:25 +0200 Subject: Generate pins for new opamp pin naming scheme The new code implements the corresponding traits for the common opamp pin naming scheme of all families, which is VINPx/VINMx. The same pin must not be used for multiple channels for the same opamp. For example, if VINM0 and VINM1 of the same opamp were assigned to the same pin, the channel would not be unique, meaning that the traits would be implemented in a conflicting manner. --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/build.rs | 33 +++++++++++++-------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 034f51df9..4ee43e600 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a502cec14512a6b833beb8f6e15f4a7b5ee7c06" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a502cec14512a6b833beb8f6e15f4a7b5ee7c06", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index bb5ef53d7..8143c9a23 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1402,31 +1402,24 @@ fn main() { } if regs.kind == "opamp" { - if pin.signal.starts_with("VP") { - // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) - let peri = format_ident!("{}", p.name); - let pin_name = format_ident!("{}", pin.pin); - let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); - - g.extend(quote! { - impl_opamp_vp_pin!( #peri, #pin_name, #ch); - }) - } else if pin.signal.starts_with("VINM") { - // Impl NonInvertingPin for the VINM* signals ( VINM0, VINM1, etc) - // STM32G4 - let peri = format_ident!("{}", p.name); - let pin_name = format_ident!("{}", pin.pin); - let ch: Result = pin.signal.strip_prefix("VINM").unwrap().parse(); - - if let Ok(ch) = ch { + let peri = format_ident!("{}", p.name); + let pin_name = format_ident!("{}", pin.pin); + if let Some(ch_str) = pin.signal.strip_prefix("VINP") { + // Impl NonInvertingPin for VINP0, VINP1 etc. + if let Ok(ch) = ch_str.parse::() { + g.extend(quote! { + impl_opamp_vp_pin!( #peri, #pin_name, #ch ); + }); + } + } else if let Some(ch_str) = pin.signal.strip_prefix("VINM") { + // Impl InvertingPin for VINM0, VINM1 etc. + if let Ok(ch) = ch_str.parse::() { g.extend(quote! { impl_opamp_vn_pin!( #peri, #pin_name, #ch); - }) + }); } } else if pin.signal == "VOUT" { // Impl OutputPin for the VOUT pin - let peri = format_ident!("{}", p.name); - let pin_name = format_ident!("{}", pin.pin); g.extend(quote! { impl_opamp_vout_pin!( #peri, #pin_name ); }) -- cgit From ca14f5452959bb23499f057ca78cf21e0e69dccd Mon Sep 17 00:00:00 2001 From: Thomas Giesel Date: Wed, 25 Jun 2025 21:06:39 +0200 Subject: Adapt opamp driver to new opamp IP version numbers --- embassy-stm32/src/opamp.rs | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index 2eb2e61c1..0467dbce3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -7,7 +7,7 @@ use crate::pac::opamp::vals::*; use crate::Peri; /// Performs a busy-wait delay for a specified number of microseconds. -#[cfg(opamp_g4)] +#[cfg(opamp_v5)] fn blocking_delay_ms(ms: u32) { #[cfg(feature = "time")] embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); @@ -23,13 +23,13 @@ pub enum OpAmpGain { Mul4, Mul8, Mul16, - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] Mul32, - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] Mul64, } -#[cfg(opamp_g4)] +#[cfg(opamp_v5)] enum OpAmpDifferentialPair { P, N, @@ -53,7 +53,7 @@ pub struct OpAmpOutput<'d, T: Instance> { /// OpAmp internal outputs, wired directly to ADC inputs. /// /// This struct can be used as an ADC input. -#[cfg(opamp_g4)] +#[cfg(opamp_v5)] pub struct OpAmpInternalOutput<'d, T: Instance> { _inner: &'d OpAmp<'d, T>, } @@ -67,8 +67,8 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// Create a new driver instance. /// /// Does not enable the opamp, but does set the speed mode on some families. - pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { - #[cfg(opamp_g4)] + pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_v5)] speed: OpAmpSpeed) -> Self { + #[cfg(opamp_v5)] T::regs().csr().modify(|w| { w.set_opahsm(speed == OpAmpSpeed::HighSpeed); }); @@ -94,15 +94,15 @@ impl<'d, T: Instance> OpAmp<'d, T> { in_pin.set_as_analog(); out_pin.set_as_analog(); - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] let vm_sel = VmSel::OUTPUT; - #[cfg(not(opamp_g4))] + #[cfg(not(opamp_v5))] let vm_sel = VmSel::from_bits(0b11); T::regs().csr().modify(|w| { w.set_vp_sel(VpSel::from_bits(in_pin.channel())); w.set_vm_sel(vm_sel); - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] w.set_opaintoen(false); w.set_opampen(true); }); @@ -129,12 +129,12 @@ impl<'d, T: Instance> OpAmp<'d, T> { in_pin.set_as_analog(); out_pin.set_as_analog(); - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] let vm_sel = VmSel::PGA; - #[cfg(not(opamp_g4))] + #[cfg(not(opamp_v5))] let vm_sel = VmSel::from_bits(0b10); - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] let pga_gain = match gain { OpAmpGain::Mul2 => PgaGain::GAIN2, OpAmpGain::Mul4 => PgaGain::GAIN4, @@ -143,7 +143,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { OpAmpGain::Mul32 => PgaGain::GAIN32, OpAmpGain::Mul64 => PgaGain::GAIN64, }; - #[cfg(not(opamp_g4))] + #[cfg(not(opamp_v5))] let pga_gain = PgaGain::from_bits(match gain { OpAmpGain::Mul2 => 0b00, OpAmpGain::Mul4 => 0b01, @@ -155,7 +155,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { w.set_vp_sel(VpSel::from_bits(in_pin.channel())); w.set_vm_sel(vm_sel); w.set_pga_gain(pga_gain); - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] w.set_opaintoen(false); w.set_opampen(true); }); @@ -170,7 +170,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// preventing it being used elsewhere. The `OpAmpOutput` can then be /// directly used as an ADC input. The opamp will be disabled when the /// [`OpAmpOutput`] is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin + crate::gpio::Pin>) -> OpAmpOutput<'_, T> { out_pin.set_as_analog(); @@ -194,7 +194,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. /// The opamp output will be disabled when it is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn buffer_int( &mut self, pin: Peri<'_, impl NonInvertingPin + crate::gpio::Pin>, @@ -204,7 +204,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { T::regs().csr().modify(|w| { w.set_vp_sel(VpSel::from_bits(pin.channel())); w.set_vm_sel(VmSel::OUTPUT); - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] w.set_opaintoen(true); w.set_opampen(true); }); @@ -220,7 +220,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. /// The opamp output will be disabled when it is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn pga_int( &mut self, pin: Peri<'_, impl NonInvertingPin + crate::gpio::Pin>, @@ -257,7 +257,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// /// The returned `OpAmpInternalOutput` struct may be used as an ADC /// input. The opamp output will be disabled when it is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn standalone_dac_int( &mut self, m_pin: Peri<'_, impl InvertingPin + crate::gpio::Pin>, @@ -285,7 +285,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// The output pin is held within the returned [`OpAmpOutput`] struct, /// preventing it being used elsewhere. The opamp will be disabled when /// the [`OpAmpOutput`] is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn standalone_dac_ext( &mut self, m_pin: Peri<'_, impl InvertingPin + crate::gpio::Pin>, @@ -315,7 +315,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// The output pin is held within the returned [`OpAmpOutput`] struct, /// preventing it being used elsewhere. The opamp will be disabled when /// the [`OpAmpOutput`] is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn standalone_ext( &mut self, p_pin: Peri<'d, impl NonInvertingPin + crate::gpio::Pin>, @@ -346,7 +346,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// /// The returned `OpAmpOutput` struct may be used as an ADC /// input. The opamp output will be disabled when it is dropped. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn standalone_int( &mut self, p_pin: Peri<'d, impl NonInvertingPin + crate::gpio::Pin>, @@ -374,7 +374,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// while for high-speed mode, only the P differential pair is calibrated. /// /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method). - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] pub fn calibrate(&mut self) { T::regs().csr().modify(|w| { w.set_opampen(true); @@ -403,7 +403,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// The calibration range is from 0 to 31. /// /// The result is stored in the OPAMP_CSR register. - #[cfg(opamp_g4)] + #[cfg(opamp_v5)] fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) { let mut low = 0; let mut high = 31; @@ -460,7 +460,7 @@ impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { } } -#[cfg(opamp_g4)] +#[cfg(opamp_v5)] impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { fn drop(&mut self) { T::regs().csr().modify(|w| { @@ -545,7 +545,7 @@ foreach_peripheral!( }; ); -#[cfg(opamp_g4)] +#[cfg(opamp_v5)] macro_rules! impl_opamp_internal_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( @@ -567,7 +567,7 @@ macro_rules! impl_opamp_internal_output { }; } -#[cfg(opamp_g4)] +#[cfg(opamp_v5)] foreach_peripheral!( (opamp, OPAMP1) => { impl_opamp_internal_output!(OPAMP1, ADC1, 13); -- cgit From 51675e9bc767080b0e9c47f106ad3ef4251f9d8b Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Wed, 25 Jun 2025 19:22:18 -0700 Subject: Added STM32WBA6XXX devices as features Added 2 wba6 features to cargo batch in ci.sh WIP Added default RTC rust version for undefined peripherals Added missing generated RTC peripheral to be handled by rtc/v3.rs Reordered cfg_attrs in rtc/mod.rs --- ci.sh | 2 ++ embassy-stm32/Cargo.toml | 22 ++++++++++++++++++++-- embassy-stm32/src/rtc/mod.rs | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ci.sh b/ci.sh index e1fdf998a..33152e559 100755 --- a/ci.sh +++ b/ci.sh @@ -169,6 +169,8 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4ee43e600..552113a79 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a502cec14512a6b833beb8f6e15f4a7b5ee7c06" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0069be7389d0378d826003304d72a13008f3ebce" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a502cec14512a6b833beb8f6e15f4a7b5ee7c06", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0069be7389d0378d826003304d72a13008f3ebce", default-features = false, features = ["metadata"] } [features] default = ["rt"] @@ -1634,6 +1634,24 @@ stm32wba55he = [ "stm32-metapac/stm32wba55he" ] stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ] stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ] stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ] +stm32wba62cg = [ "stm32-metapac/stm32wba62cg" ] +stm32wba62ci = [ "stm32-metapac/stm32wba62ci" ] +stm32wba62mg = [ "stm32-metapac/stm32wba62mg" ] +stm32wba62mi = [ "stm32-metapac/stm32wba62mi" ] +stm32wba62pg = [ "stm32-metapac/stm32wba62pg" ] +stm32wba62pi = [ "stm32-metapac/stm32wba62pi" ] +stm32wba63cg = [ "stm32-metapac/stm32wba63cg" ] +stm32wba63ci = [ "stm32-metapac/stm32wba63ci" ] +stm32wba64cg = [ "stm32-metapac/stm32wba64cg" ] +stm32wba64ci = [ "stm32-metapac/stm32wba64ci" ] +stm32wba65cg = [ "stm32-metapac/stm32wba65cg" ] +stm32wba65ci = [ "stm32-metapac/stm32wba65ci" ] +stm32wba65mg = [ "stm32-metapac/stm32wba65mg" ] +stm32wba65mi = [ "stm32-metapac/stm32wba65mi" ] +stm32wba65pg = [ "stm32-metapac/stm32wba65pg" ] +stm32wba65pi = [ "stm32-metapac/stm32wba65pi" ] +stm32wba65rg = [ "stm32-metapac/stm32wba65rg" ] +stm32wba65ri = [ "stm32-metapac/stm32wba65ri" ] stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core", "_core-cm4" ] stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core", "_core-cm0p" ] stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core", "_core-cm4" ] diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 49f423f37..2c5aaca35 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -25,7 +25,7 @@ use crate::time::Hertz; ), path = "v2.rs" )] -#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs), path = "v3.rs")] +#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs, rtc_v3c0), path = "v3.rs")] mod _version; #[allow(unused_imports)] pub use _version::*; -- cgit From f0e3ca9ee4efcb4a9618983190068d866631fa28 Mon Sep 17 00:00:00 2001 From: Kevin Lannen Date: Mon, 23 Jun 2025 22:03:43 -0600 Subject: STM32H7RS Examples: Add OPI functionality to the XSPI example Cleans up the SPI and OPI commands to match the datasheet for the flash used on the Nucleo board Creates a separate impl for OPI operations --- examples/stm32h7rs/src/bin/xspi_memory_mapped.rs | 850 ++++++++++++++++++----- 1 file changed, 677 insertions(+), 173 deletions(-) diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs index 88d914180..59045ca2e 100644 --- a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs +++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs @@ -3,7 +3,8 @@ //! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00 //! -//! TODO: Currently this only uses single SPI, pending flash chip documentation for octo SPI. + +use core::cmp::min; use defmt::info; use embassy_executor::Spawner; @@ -52,14 +53,16 @@ async fn main(_spawner: Spawner) { fifo_threshold: FIFOThresholdLevel::_4Bytes, memory_type: MemoryType::Macronix, delay_hold_quarter_cycle: true, - // memory_type: MemoryType::Micron, - // delay_hold_quarter_cycle: false, device_size: MemorySize::_32MiB, chip_select_high_time: ChipSelectHighTime::_2Cycle, free_running_clock: false, clock_mode: false, wrap_size: WrapSize::None, - // 300mhz / (4+1) = 60mhz. Unsure the limit, need to find a MX25UW25645GXDI00 datasheet. + // 300 MHz clock / (3 + 1) = 75 MHz. This is above the max for READ instructions so the + // FAST READ must be used. The nucleo board's flash can run at up to 133 MHz in SPI mode + // and 200 MHz in OPI mode. This clock prescaler must be even otherwise the clock will not + // have symmetric high and low times. + // The clock can also be fed by one of the PLLs to allow for more flexible clock rates. clock_prescaler: 3, sample_shifting: false, chip_select_boundary: 0, @@ -71,28 +74,41 @@ async fn main(_spawner: Spawner) { // Not necessary, but recommended if using XIP cor.SCB.enable_icache(); + // Note: Enabling data cache can cause issues with DMA transfers. cor.SCB.enable_dcache(&mut cor.CPUID); let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi( p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config, ); - let mut flash = FlashMemory::new(xspi).await; + let mut flash = SpiFlashMemory::new(xspi); let flash_id = flash.read_id(); info!("FLASH ID: {=[u8]:x}", flash_id); - let mut wr_buf = [0u8; 8]; - for i in 0..8 { - wr_buf[i] = 0x90 + i as u8; - } - let mut rd_buf = [0u8; 8]; - flash.erase_sector(0).await; - flash.write_memory(0, &wr_buf, true).await; - flash.read_memory(0, &mut rd_buf, true); - info!("WRITE BUF: {=[u8]:#X}", wr_buf); - info!("READ BUF: {=[u8]:#X}", rd_buf); - flash.enable_mm().await; + // Erase the first sector + flash.erase_sector(0); + + // Write some data into the flash. This writes more than one page to test that functionality. + let mut wr_buf = [0u8; 512]; + let base_number: u8 = 0x90; + for i in 0..512 { + wr_buf[i] = base_number.wrapping_add(i as u8); + } + flash.write_memory(0, &wr_buf); + + // Read the data back and verify it. + let mut rd_buf = [0u8; 512]; + let start_time = embassy_time::Instant::now(); + flash.read_memory(0, &mut rd_buf); + let elapsed = start_time.elapsed(); + info!("Read 512 bytes in {} us in SPI mode", elapsed.as_micros()); + info!("WRITE BUF: {=[u8]:#X}", wr_buf[0..32]); + info!("READ BUF: {=[u8]:#X}", rd_buf[0..32]); + + assert_eq!(wr_buf, rd_buf, "Read buffer does not match write buffer"); + + flash.enable_mm(); info!("Enabled memory mapped mode"); let first_u32 = unsafe { *(0x70000000 as *const u32) }; @@ -103,10 +119,53 @@ async fn main(_spawner: Spawner) { assert_eq!(second_u32, 0x97969594); info!("second_u32 {:08x}", first_u32); - flash.disable_mm().await; + flash.disable_mm(); info!("Disabled memory mapped mode"); + let flash_id = flash.read_id(); + info!("FLASH ID: {=[u8]:x}", flash_id); + + let mut flash = flash.into_octo(); + + Timer::after_millis(100).await; + + let flash_id = flash.read_id(); + info!("FLASH ID in OPI mode: {=[u8]:x}", flash_id); + + flash.erase_sector(0); + + let mut rd_buf = [0u8; 512]; + flash.read_memory(0, &mut rd_buf); + info!("READ BUF after erase: {=[u8]:#X}", rd_buf[0..32]); + + assert_eq!(rd_buf, [0xFF; 512], "Read buffer is not all 0xFF after erase"); + + flash.write_memory(0, &wr_buf); + let start = embassy_time::Instant::now(); + flash.read_memory(0, &mut rd_buf); + let elapsed = start.elapsed(); + info!("Read 512 bytes in {} us in OPI mode", elapsed.as_micros()); + info!("READ BUF after write: {=[u8]:#X}", rd_buf[0..32]); + assert_eq!(wr_buf, rd_buf, "Read buffer does not match write buffer in OPI mode"); + + flash.enable_mm(); + info!("Enabled memory mapped mode in OPI mode"); + let first_u32 = unsafe { *(0x70000000 as *const u32) }; + assert_eq!(first_u32, 0x93929190); + info!("first_u32 {:08x}", first_u32); + let second_u32 = unsafe { *(0x70000004 as *const u32) }; + assert_eq!(second_u32, 0x97969594); + info!("second_u32 {:08x}", first_u32); + flash.disable_mm(); + info!("Disabled memory mapped mode in OPI mode"); + + // Reset back to SPI mode + let mut flash = flash.into_spi(); + let flash_id = flash.read_id(); + info!("FLASH ID back in SPI mode: {=[u8]:x}", flash_id); + info!("DONE"); + // Output pin PE3 let mut led = Output::new(p.PE3, Level::Low, Speed::Low); @@ -116,80 +175,268 @@ async fn main(_spawner: Spawner) { } } -const MEMORY_PAGE_SIZE: usize = 8; - -const CMD_READ: u8 = 0x0B; -const _CMD_QUAD_READ: u8 = 0x6B; - -const CMD_WRITE_PG: u8 = 0x02; -const _CMD_QUAD_WRITE_PG: u8 = 0x32; - -const CMD_READ_ID: u8 = 0x9F; -const CMD_READ_ID_OCTO: u16 = 0x9F60; +const MEMORY_PAGE_SIZE: usize = 256; -const CMD_ENABLE_RESET: u8 = 0x66; -const CMD_RESET: u8 = 0x99; - -const CMD_WRITE_ENABLE: u8 = 0x06; - -const CMD_CHIP_ERASE: u8 = 0xC7; -const CMD_SECTOR_ERASE: u8 = 0x20; -const CMD_BLOCK_ERASE_32K: u8 = 0x52; -const CMD_BLOCK_ERASE_64K: u8 = 0xD8; - -const CMD_READ_SR: u8 = 0x05; -const CMD_READ_CR: u8 = 0x35; - -const CMD_WRITE_SR: u8 = 0x01; -const CMD_WRITE_CR: u8 = 0x31; +/// Implementation of access to flash chip using SPI. +/// +/// Chip commands are hardcoded as it depends on used chip. +/// This targets a MX25UW25645GXDI00. +pub struct SpiFlashMemory { + xspi: Xspi<'static, I, Blocking>, +} -/// Implementation of access to flash chip. +/// Implementation of access to flash chip using Octo SPI. /// /// Chip commands are hardcoded as it depends on used chip. /// This targets a MX25UW25645GXDI00. -pub struct FlashMemory { +pub struct OpiFlashMemory { xspi: Xspi<'static, I, Blocking>, } -impl FlashMemory { - pub async fn new(xspi: Xspi<'static, I, Blocking>) -> Self { - let mut memory = Self { xspi }; +/// SPI mode commands for MX25UW25645G flash memory +#[allow(dead_code)] +#[repr(u8)] +enum SpiCommand { + // Array access commands + /// Read data bytes using 3-byte address (up to 50 MHz) + Read3B = 0x03, + /// Fast read data bytes using 3-byte address with 8 dummy cycles (up to 133 MHz) + FastRead3B = 0x0B, + /// Program 1-256 bytes of data using 3-byte address + PageProgram3B = 0x02, + /// Erase 4KB sector using 3-byte address + SectorErase3B = 0x20, + /// Erase 64KB block using 3-byte address + BlockErase3B = 0xD8, + /// Read data bytes using 4-byte address (up to 50 MHz) + Read4B = 0x13, + /// Fast read data bytes using 4-byte address with 8 dummy cycles (up to 133 MHz) + FastRead4B = 0x0C, + /// Program 1-256 bytes of data using 4-byte address + PageProgram4B = 0x12, + /// Erase 4KB sector using 4-byte address + SectorErase4B = 0x21, + /// Erase 64KB block using 4-byte address + BlockErase4B = 0xDC, + /// Erase entire chip (only if no blocks are protected) + ChipErase = 0x60, + + // Write Buffer Access commands + /// Read data from the 256-byte page buffer + ReadBuffer = 0x25, + /// Initialize write-to-buffer sequence, clears buffer and writes initial data + WriteBufferInitial = 0x22, + /// Continue writing data to buffer (used between WRBI and WRCF) + WriteBufferContinue = 0x24, + /// Confirm write operation, programs buffer contents to flash array + WriteBufferConfirm = 0x31, + + // Device operation commands + /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations + WriteEnable = 0x06, + /// Clear Write Enable Latch (WEL) bit + WriteDisable = 0x04, + /// Select write protection mode (BP mode or Advanced Sector Protection) + WriteProtectSelection = 0x68, + /// Suspend ongoing program or erase operation to allow read access + ProgramEraseSuspend = 0xB0, + /// Resume suspended program or erase operation + ProgramEraseResume = 0x30, + /// Enter deep power-down mode for minimum power consumption + DeepPowerDown = 0xB9, + /// Exit deep power-down mode and return to standby + ReleaseFromDeepPowerDown = 0xAB, + /// No operation, can terminate Reset Enable command + NoOperation = 0x00, + /// Enable reset operation (must precede Reset Memory command) + ResetEnable = 0x66, + /// Reset device to power-on state (requires prior Reset Enable) + ResetMemory = 0x99, + /// Protect all sectors using Dynamic Protection Bits (DPB) + GangBlockLock = 0x7E, + /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB) + GangBlockUnlock = 0x98, + + // Register Access commands + /// Read 3-byte device identification (manufacturer ID + device ID) + ReadIdentification = 0x9F, + /// Read Serial Flash Discoverable Parameters (SFDP) table + ReadSFDP = 0x5A, + /// Read 8-bit Status Register (WIP, WEL, BP bits, etc.) + ReadStatusRegister = 0x05, + /// Read 8-bit Configuration Register (ODS, TB, PBE bits) + ReadConfigurationRegister = 0x15, + /// Write Status and/or Configuration Register (1-2 bytes) + WriteStatusConfigurationRegister = 0x01, + /// Read Configuration Register 2 from specified 4-byte address + ReadConfigurationRegister2 = 0x71, + /// Write Configuration Register 2 to specified 4-byte address + WriteConfigurationRegister2 = 0x72, + /// Read 8-bit Security Register (protection status, suspend bits) + ReadSecurityRegister = 0x2B, + /// Write Security Register to set customer lock-down bit + WriteSecurityRegister = 0x2F, + /// Read 32-bit Fast Boot Register (boot address and configuration) + ReadFastBootRegister = 0x16, + /// Write 32-bit Fast Boot Register + WriteFastBootRegister = 0x17, + /// Erase Fast Boot Register (disable fast boot feature) + EraseFastBootRegister = 0x18, + /// Set burst/wrap length for read operations (16/32/64 bytes) + SetBurstLength = 0xC0, + /// Enter 8K-bit secured OTP mode for programming unique identifiers + EnterSecuredOTP = 0xB1, + /// Exit secured OTP mode and return to main array access + ExitSecuredOTP = 0xC1, + /// Write Lock Register to control SPB protection mode + WriteLockRegister = 0x2C, + /// Read Lock Register status + ReadLockRegister = 0x2D, + /// Program Solid Protection Bit (SPB) for specified sector/block + WriteSPB = 0xE3, + /// Erase all Solid Protection Bits (SPB) + EraseSPB = 0xE4, + /// Read Solid Protection Bit (SPB) status for specified sector/block + ReadSPB = 0xE2, + /// Write Dynamic Protection Bit (DPB) for specified sector + WriteDPB = 0xE1, + /// Read Dynamic Protection Bit (DPB) status for specified sector + ReadDPB = 0xE0, + /// Read 64-bit password register (only in Solid Protection mode) + ReadPassword = 0x27, + /// Write 64-bit password register + WritePassword = 0x28, + /// Unlock SPB operations using 64-bit password + PasswordUnlock = 0x29, +} - memory.reset_memory().await; - memory.enable_octo(); - memory - } +/// OPI mode commands for MX25UW25645G flash memory +#[allow(dead_code)] +#[repr(u16)] +enum OpiCommand { + // Array access commands + /// Read data using 8 I/O lines in STR mode with configurable dummy cycles (up to 200 MHz) + OctaRead = 0xEC13, + /// Read data using 8 I/O lines in DTR mode with configurable dummy cycles (up to 200 MHz) + OctaDTRRead = 0xEE11, + /// Program 1-256 bytes using 4-byte address and 8 I/O lines + PageProgram4B = 0x12ED, + /// Erase 4KB sector using 4-byte address + SectorErase4B = 0x21DE, + /// Erase 64KB block using 4-byte address + BlockErase4B = 0xDC23, + /// Erase entire chip (only if no blocks are protected) + ChipErase = 0x609F, + + // Write Buffer Access commands + /// Read data from the 256-byte page buffer using 4-byte address + ReadBuffer = 0x25DA, + /// Initialize interruptible write-to-buffer sequence with 4-byte address + WriteBufferInitial = 0x22DD, + /// Continue writing data to buffer during interruptible sequence + WriteBufferContinue = 0x24DB, + /// Confirm and execute write operation from buffer to flash array + WriteBufferConfirm = 0x31CE, + + // Device operation commands + /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations + WriteEnable = 0x06F9, + /// Clear Write Enable Latch (WEL) bit, aborts write-to-buffer sequence + WriteDisable = 0x04FB, + /// Select write protection mode (BP mode or Advanced Sector Protection) - OTP bit + WriteProtectSelection = 0x6897, + /// Suspend ongoing program or erase operation to allow read from other banks + ProgramEraseSuspend = 0xB04F, + /// Resume suspended program or erase operation + ProgramEraseResume = 0x30CF, + /// Enter deep power-down mode for minimum power consumption + DeepPowerDown = 0xB946, + /// Exit deep power-down mode and return to standby + ReleaseFromDeepPowerDown = 0xAB54, + /// No operation, can terminate Reset Enable command + NoOperation = 0x00FF, + /// Enable reset operation (must precede Reset Memory command) + ResetEnable = 0x6699, + /// Reset device to power-on state, clears volatile settings + ResetMemory = 0x9966, + /// Protect all sectors using Dynamic Protection Bits (DPB) + GangBlockLock = 0x7E81, + /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB) + GangBlockUnlock = 0x9867, + + // Register Access commands + /// Read 3-byte device identification with 4-byte dummy address + ReadIdentification = 0x9F60, + /// Read Serial Flash Discoverable Parameters (SFDP) table with 4-byte address + ReadSFDP = 0x5AA5, + /// Read 8-bit Status Register with 4-byte dummy address + ReadStatusRegister = 0x05FA, + /// Read 8-bit Configuration Register with specific address (00000001h) + ReadConfigurationRegister = 0x15EA, + /// Write 8-bit Status Register with specific address (00000000h) or Configuration Register with address (00000001h) + WriteStatusConfigurationRegister = 0x01FE, + /// Read Configuration Register 2 from specified 4-byte address + ReadConfigurationRegister2 = 0x718E, + /// Write Configuration Register 2 to specified 4-byte address + WriteConfigurationRegister2 = 0x728D, + /// Read 8-bit Security Register with 4-byte dummy address + ReadSecurityRegister = 0x2BD4, + /// Write Security Register to set customer lock-down bit + WriteSecurityRegister = 0x2FD0, + /// Set burst/wrap length for read operations with 4-byte dummy address + SetBurstLength = 0xC03F, + /// Read 32-bit Fast Boot Register with 4-byte dummy address + ReadFastBootRegister = 0x16E9, + /// Write 32-bit Fast Boot Register with 4-byte dummy address + WriteFastBootRegister = 0x17E8, + /// Erase Fast Boot Register (disable fast boot feature) + EraseFastBootRegister = 0x18E7, + /// Enter 8K-bit secured OTP mode for programming unique identifiers + EnterSecuredOTP = 0xB14E, + /// Exit secured OTP mode and return to main array access + ExitSecuredOTP = 0xC13E, + /// Write Lock Register to control SPB protection mode with 4-byte dummy address + WriteLockRegister = 0x2CD3, + /// Read Lock Register status with 4-byte dummy address + ReadLockRegister = 0x2DD2, + /// Program Solid Protection Bit (SPB) for specified 4-byte address + WriteSPB = 0xE31C, + /// Erase all Solid Protection Bits (SPB) + EraseSPB = 0xE41B, + /// Read Solid Protection Bit (SPB) status for specified 4-byte address + ReadSPB = 0xE21D, + /// Write Dynamic Protection Bit (DPB) for specified 4-byte address + WriteDPB = 0xE11E, + /// Read Dynamic Protection Bit (DPB) status for specified 4-byte address + ReadDPB = 0xE01F, + /// Read 64-bit password register with 4-byte dummy address and 20 dummy cycles + ReadPassword = 0x27D8, + /// Write 64-bit password register with 4-byte dummy address + WritePassword = 0x28D7, + /// Unlock SPB operations using 64-bit password with 4-byte dummy address + PasswordUnlock = 0x29D6, +} - async fn qpi_mode(&mut self) { - // Enter qpi mode - self.exec_command(0x38).await; +impl SpiFlashMemory { + pub fn new(xspi: Xspi<'static, I, Blocking>) -> Self { + let mut memory = Self { xspi }; - // Set read param - let transaction = TransferConfig { - iwidth: XspiWidth::QUAD, - dwidth: XspiWidth::QUAD, - instruction: Some(0xC0), - ..Default::default() - }; - self.enable_write().await; - self.xspi.blocking_write(&[0x30_u8], transaction).unwrap(); - self.wait_write_finish(); + memory.reset_memory(); + memory } - pub async fn disable_mm(&mut self) { + pub fn disable_mm(&mut self) { self.xspi.disable_memory_mapped_mode(); } - pub async fn enable_mm(&mut self) { - self.qpi_mode().await; - + pub fn enable_mm(&mut self) { let read_config = TransferConfig { iwidth: XspiWidth::SING, isize: AddressSize::_8bit, adwidth: XspiWidth::SING, - adsize: AddressSize::_24bit, + adsize: AddressSize::_32bit, dwidth: XspiWidth::SING, - instruction: Some(CMD_READ as u32), + instruction: Some(SpiCommand::FastRead4B as u32), dummy: DummyCycles::_8, ..Default::default() }; @@ -198,42 +445,28 @@ impl FlashMemory { iwidth: XspiWidth::SING, isize: AddressSize::_8bit, adwidth: XspiWidth::SING, - adsize: AddressSize::_24bit, + adsize: AddressSize::_32bit, dwidth: XspiWidth::SING, - instruction: Some(CMD_WRITE_PG as u32), + instruction: Some(SpiCommand::PageProgram4B as u32), dummy: DummyCycles::_0, ..Default::default() }; self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); } - fn enable_octo(&mut self) { - let cr = self.read_cr(); - // info!("Read cr: {:x}", cr); - self.write_cr(cr | 0x02); - // info!("Read cr after writing: {:x}", cr); + fn into_octo(mut self) -> OpiFlashMemory { + self.enable_opi_mode(); + OpiFlashMemory { xspi: self.xspi } } - pub fn disable_octo(&mut self) { - let cr = self.read_cr(); - self.write_cr(cr & (!(0x02))); - } - - async fn exec_command_4(&mut self, cmd: u8) { - let transaction = TransferConfig { - iwidth: XspiWidth::QUAD, - adwidth: XspiWidth::NONE, - // adsize: AddressSize::_24bit, - dwidth: XspiWidth::NONE, - instruction: Some(cmd as u32), - address: None, - dummy: DummyCycles::_0, - ..Default::default() - }; - self.xspi.blocking_command(&transaction).unwrap(); + fn enable_opi_mode(&mut self) { + let cr2_0 = self.read_cr2(0); + info!("Read CR2 at 0x0: {:x}", cr2_0); + self.enable_write(); + self.write_cr2(0, cr2_0 | 0x01); // Set bit 0 to enable octo SPI in STR } - async fn exec_command(&mut self, cmd: u8) { + fn exec_command(&mut self, cmd: u8) { let transaction = TransferConfig { iwidth: XspiWidth::SING, adwidth: XspiWidth::NONE, @@ -248,16 +481,14 @@ impl FlashMemory { self.xspi.blocking_command(&transaction).unwrap(); } - pub async fn reset_memory(&mut self) { - self.exec_command_4(CMD_ENABLE_RESET).await; - self.exec_command_4(CMD_RESET).await; - self.exec_command(CMD_ENABLE_RESET).await; - self.exec_command(CMD_RESET).await; + pub fn reset_memory(&mut self) { + self.exec_command(SpiCommand::ResetEnable as u8); + self.exec_command(SpiCommand::ResetMemory as u8); self.wait_write_finish(); } - pub async fn enable_write(&mut self) { - self.exec_command(CMD_WRITE_ENABLE).await; + pub fn enable_write(&mut self) { + self.exec_command(SpiCommand::WriteEnable as u8); } pub fn read_id(&mut self) -> [u8; 3] { @@ -266,92 +497,64 @@ impl FlashMemory { iwidth: XspiWidth::SING, isize: AddressSize::_8bit, adwidth: XspiWidth::NONE, - // adsize: AddressSize::_24bit, dwidth: XspiWidth::SING, - instruction: Some(CMD_READ_ID as u32), + instruction: Some(SpiCommand::ReadIdentification as u32), ..Default::default() }; - // info!("Reading id: 0x{:X}", transaction.instruction); self.xspi.blocking_read(&mut buffer, transaction).unwrap(); buffer } - pub fn read_id_8(&mut self) -> [u8; 3] { - let mut buffer = [0; 3]; - let transaction: TransferConfig = TransferConfig { - iwidth: XspiWidth::OCTO, - isize: AddressSize::_16bit, - adwidth: XspiWidth::OCTO, - address: Some(0), - adsize: AddressSize::_32bit, - dwidth: XspiWidth::OCTO, - instruction: Some(CMD_READ_ID_OCTO as u32), - dummy: DummyCycles::_4, - ..Default::default() - }; - info!("Reading id: {:#X}", transaction.instruction); - self.xspi.blocking_read(&mut buffer, transaction).unwrap(); - buffer - } - - pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { + pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) { let transaction = TransferConfig { iwidth: XspiWidth::SING, adwidth: XspiWidth::SING, - adsize: AddressSize::_24bit, + adsize: AddressSize::_32bit, dwidth: XspiWidth::SING, - instruction: Some(CMD_READ as u32), + instruction: Some(SpiCommand::FastRead4B as u32), dummy: DummyCycles::_8, - // dwidth: XspiWidth::QUAD, - // instruction: Some(CMD_QUAD_READ as u32), - // dummy: DummyCycles::_8, address: Some(addr), ..Default::default() }; - if use_dma { - self.xspi.blocking_read(buffer, transaction).unwrap(); - } else { - self.xspi.blocking_read(buffer, transaction).unwrap(); - } + + self.xspi.blocking_read(buffer, transaction).unwrap(); } fn wait_write_finish(&mut self) { while (self.read_sr() & 0x01) != 0 {} } - async fn perform_erase(&mut self, addr: u32, cmd: u8) { + fn perform_erase(&mut self, addr: u32, cmd: u8) { let transaction = TransferConfig { iwidth: XspiWidth::SING, adwidth: XspiWidth::SING, - adsize: AddressSize::_24bit, + adsize: AddressSize::_32bit, dwidth: XspiWidth::NONE, instruction: Some(cmd as u32), address: Some(addr), dummy: DummyCycles::_0, ..Default::default() }; - self.enable_write().await; + self.enable_write(); self.xspi.blocking_command(&transaction).unwrap(); self.wait_write_finish(); } - pub async fn erase_sector(&mut self, addr: u32) { - self.perform_erase(addr, CMD_SECTOR_ERASE).await; + pub fn erase_sector(&mut self, addr: u32) { + self.perform_erase(addr, SpiCommand::SectorErase4B as u8); } - pub async fn erase_block_32k(&mut self, addr: u32) { - self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; + pub fn erase_block_64k(&mut self, addr: u32) { + self.perform_erase(addr, SpiCommand::BlockErase4B as u8); } - pub async fn erase_block_64k(&mut self, addr: u32) { - self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; - } - - pub async fn erase_chip(&mut self) { - self.exec_command(CMD_CHIP_ERASE).await; + pub fn erase_chip(&mut self) { + self.enable_write(); + self.exec_command(SpiCommand::ChipErase as u8); + self.wait_write_finish(); } - async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { + fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) { assert!( (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", @@ -361,48 +564,43 @@ impl FlashMemory { let transaction = TransferConfig { iwidth: XspiWidth::SING, - adsize: AddressSize::_24bit, + adsize: AddressSize::_32bit, adwidth: XspiWidth::SING, dwidth: XspiWidth::SING, - instruction: Some(CMD_WRITE_PG as u32), - // dwidth: XspiWidth::QUAD, - // instruction: Some(CMD_QUAD_WRITE_PG as u32), + instruction: Some(SpiCommand::PageProgram4B as u32), address: Some(addr), dummy: DummyCycles::_0, ..Default::default() }; - self.enable_write().await; - if use_dma { - self.xspi.blocking_write(buffer, transaction).unwrap(); - } else { - self.xspi.blocking_write(buffer, transaction).unwrap(); - } + self.enable_write(); + self.xspi.blocking_write(buffer, transaction).unwrap(); self.wait_write_finish(); } - pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { + pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) { let mut left = buffer.len(); let mut place = addr; let mut chunk_start = 0; while left > 0 { let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; - let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; + let chunk_size = min(max_chunk_size, left); let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; - self.write_page(place, chunk, chunk_size, use_dma).await; + self.write_page(place, chunk, chunk_size); place += chunk_size as u32; left -= chunk_size; chunk_start += chunk_size; } } + // Note: read_register cannot be used to read the configuration register 2 since there is an + // address required for that read. fn read_register(&mut self, cmd: u8) -> u8 { let mut buffer = [0; 1]; let transaction: TransferConfig = TransferConfig { iwidth: XspiWidth::SING, isize: AddressSize::_8bit, adwidth: XspiWidth::NONE, - adsize: AddressSize::_24bit, dwidth: XspiWidth::SING, instruction: Some(cmd as u32), address: None, @@ -410,39 +608,345 @@ impl FlashMemory { ..Default::default() }; self.xspi.blocking_read(&mut buffer, transaction).unwrap(); - // info!("Read w25q64 register: 0x{:x}", buffer[0]); buffer[0] } - fn write_register(&mut self, cmd: u8, value: u8) { - let buffer = [value; 1]; + pub fn read_sr(&mut self) -> u8 { + self.read_register(SpiCommand::ReadStatusRegister as u8) + } + + pub fn read_cr(&mut self) -> u8 { + self.read_register(SpiCommand::ReadConfigurationRegister as u8) + } + + pub fn write_sr_cr(&mut self, sr: u8, cr: u8) { + let buffer = [sr, cr]; let transaction: TransferConfig = TransferConfig { iwidth: XspiWidth::SING, isize: AddressSize::_8bit, - instruction: Some(cmd as u32), - adsize: AddressSize::_24bit, + instruction: Some(SpiCommand::WriteStatusConfigurationRegister as u32), adwidth: XspiWidth::NONE, dwidth: XspiWidth::SING, address: None, dummy: DummyCycles::_0, ..Default::default() }; + self.enable_write(); + self.xspi.blocking_write(&buffer, transaction).unwrap(); + self.wait_write_finish(); + } + + pub fn read_cr2(&mut self, address: u32) -> u8 { + let mut buffer = [0; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + instruction: Some(SpiCommand::ReadConfigurationRegister2 as u32), + adsize: AddressSize::_32bit, + adwidth: XspiWidth::SING, + dwidth: XspiWidth::SING, + address: Some(address), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + buffer[0] + } + + pub fn write_cr2(&mut self, address: u32, value: u8) { + let buffer = [value; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + instruction: Some(SpiCommand::WriteConfigurationRegister2 as u32), + adsize: AddressSize::_32bit, + adwidth: XspiWidth::SING, + dwidth: XspiWidth::SING, + address: Some(address), + dummy: DummyCycles::_0, + ..Default::default() + }; self.xspi.blocking_write(&buffer, transaction).unwrap(); + self.wait_write_finish(); + } +} + +impl OpiFlashMemory { + pub fn into_spi(mut self) -> SpiFlashMemory { + self.disable_opi_mode(); + SpiFlashMemory { xspi: self.xspi } + } + + /// Disable OPI mode and return to SPI + pub fn disable_opi_mode(&mut self) { + // Clear SOPI and DOPI bits in CR2 volatile register + let cr2_0 = self.read_cr2(0x00000000); + self.write_cr2(0x00000000, cr2_0 & 0xFC); // Clear bits 0 and 1 + } + + /// Enable memory-mapped mode for OPI + pub fn enable_mm(&mut self) { + let read_config = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, // 2-byte command for OPI + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::OctaRead as u32), + dummy: DummyCycles::_20, // Default dummy cycles for OPI + ..Default::default() + }; + + let write_config = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::PageProgram4B as u32), + dummy: DummyCycles::_0, + ..Default::default() + }; + + self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); + } + + pub fn disable_mm(&mut self) { + self.xspi.disable_memory_mapped_mode(); + } + + /// Execute OPI command (2-byte command) + fn exec_command(&mut self, cmd: OpiCommand) { + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, // 2-byte command + adwidth: XspiWidth::NONE, + dwidth: XspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.xspi.blocking_command(&transaction).unwrap(); + } + + /// Reset memory using OPI commands + pub fn reset_memory(&mut self) { + self.exec_command(OpiCommand::ResetEnable); + self.exec_command(OpiCommand::ResetMemory); + self.wait_write_finish(); } + /// Enable write using OPI command + pub fn enable_write(&mut self) { + self.exec_command(OpiCommand::WriteEnable); + } + + /// Read device ID in OPI mode + pub fn read_id(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::ReadIdentification as u32), + address: Some(0x00000000), // Dummy address required + dummy: DummyCycles::_4, + ..Default::default() + }; + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + /// Read memory using OPI mode + pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) { + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::OctaRead as u32), + address: Some(addr), + dummy: DummyCycles::_20, // Default for 200MHz operation + ..Default::default() + }; + self.xspi.blocking_read(buffer, transaction).unwrap(); + } + + /// Wait for write completion using OPI status read + fn wait_write_finish(&mut self) { + while (self.read_sr() & 0x01) != 0 {} + } + + /// Perform erase operation using OPI command + fn perform_erase(&mut self, addr: u32, cmd: OpiCommand) { + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::NONE, + instruction: Some(cmd as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write(); + self.xspi.blocking_command(&transaction).unwrap(); + self.wait_write_finish(); + } + + /// Erase 4KB sector using OPI + pub fn erase_sector(&mut self, addr: u32) { + self.perform_erase(addr, OpiCommand::SectorErase4B); + } + + /// Erase 64KB block using OPI + pub fn erase_block_64k(&mut self, addr: u32) { + self.perform_erase(addr, OpiCommand::BlockErase4B); + } + + /// Erase entire chip using OPI + pub fn erase_chip(&mut self) { + self.enable_write(); + self.exec_command(OpiCommand::ChipErase); + self.wait_write_finish(); + } + + /// Write single page using OPI + fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) { + assert!( + (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, + "write_page(): page write length exceeds page boundary (len = {}, addr = {:X})", + len, + addr + ); + + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::PageProgram4B as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write(); + self.xspi.blocking_write(buffer, transaction).unwrap(); + self.wait_write_finish(); + } + + /// Write memory using OPI (handles page boundaries) + pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) { + let mut left = buffer.len(); + let mut place = addr; + let mut chunk_start = 0; + + while left > 0 { + let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; + let chunk_size = min(max_chunk_size, left); + let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; + self.write_page(place, chunk, chunk_size); + place += chunk_size as u32; + left -= chunk_size; + chunk_start += chunk_size; + } + } + + /// Read register using OPI mode + fn read_register(&mut self, cmd: OpiCommand, dummy_addr: u32, dummy_cycles: DummyCycles) -> u8 { + let mut buffer = [0; 1]; + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(cmd as u32), + address: Some(dummy_addr), + dummy: dummy_cycles, + ..Default::default() + }; + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + buffer[0] + } + + /// Read Status Register using OPI pub fn read_sr(&mut self) -> u8 { - self.read_register(CMD_READ_SR) + self.read_register( + OpiCommand::ReadStatusRegister, + 0x00000000, // Dummy address + DummyCycles::_4, + ) } + /// Read Configuration Register using OPI pub fn read_cr(&mut self) -> u8 { - self.read_register(CMD_READ_CR) + self.read_register( + OpiCommand::ReadConfigurationRegister, + 0x00000001, // Address for CR + DummyCycles::_4, + ) } - pub fn write_sr(&mut self, value: u8) { - self.write_register(CMD_WRITE_SR, value); + /// Write Status/Configuration Register using OPI + pub fn write_sr_cr(&mut self, sr: u8, cr: u8) { + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::WriteStatusConfigurationRegister as u32), + address: Some(0x00000000), + dummy: DummyCycles::_0, + ..Default::default() + }; + + self.enable_write(); + self.xspi.blocking_write(&[sr, cr], transaction).unwrap(); + self.wait_write_finish(); + } + + /// Read Configuration Register 2 using OPI + pub fn read_cr2(&mut self, address: u32) -> u8 { + let mut buffer = [0; 1]; + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::ReadConfigurationRegister2 as u32), + address: Some(address), + dummy: DummyCycles::_4, + ..Default::default() + }; + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + buffer[0] } - pub fn write_cr(&mut self, value: u8) { - self.write_register(CMD_WRITE_CR, value); + /// Write Configuration Register 2 using OPI + pub fn write_cr2(&mut self, address: u32, value: u8) { + let transaction = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(OpiCommand::WriteConfigurationRegister2 as u32), + address: Some(address), + dummy: DummyCycles::_0, + ..Default::default() + }; + + self.enable_write(); + self.xspi.blocking_write(&[value], transaction).unwrap(); + self.wait_write_finish(); } } -- cgit From 3e78f8a108c031f992116808b66c5e6c760c2c9e Mon Sep 17 00:00:00 2001 From: melvdl Date: Thu, 26 Jun 2025 19:35:19 +0200 Subject: stm32: generify timer channels --- embassy-stm32/build.rs | 36 ++++----- embassy-stm32/src/timer/complementary_pwm.rs | 72 ++++++++--------- embassy-stm32/src/timer/input_capture.rs | 67 ++++++++-------- embassy-stm32/src/timer/low_level.rs | 38 ++++----- embassy-stm32/src/timer/mod.rs | 111 +++++++++++++++++++++------ embassy-stm32/src/timer/one_pulse.rs | 88 ++++++++++++--------- embassy-stm32/src/timer/pwm_input.rs | 32 ++++---- embassy-stm32/src/timer/qei.rs | 10 ++- embassy-stm32/src/timer/simple_pwm.rs | 80 +++++++++---------- 9 files changed, 297 insertions(+), 237 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 8143c9a23..192688149 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1090,21 +1090,21 @@ fn main() { (("fmc", "CLK"), quote!(crate::fmc::ClkPin)), (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), - (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), - (("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)), - (("timer", "CH2"), quote!(crate::timer::Channel2Pin)), - (("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)), - (("timer", "CH3"), quote!(crate::timer::Channel3Pin)), - (("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)), - (("timer", "CH4"), quote!(crate::timer::Channel4Pin)), - (("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)), + (("timer", "CH1"), quote!(crate::timer::TimerPin)), + (("timer", "CH1N"), quote!(crate::timer::TimerComplementaryPin)), + (("timer", "CH2"), quote!(crate::timer::TimerPin)), + (("timer", "CH2N"), quote!(crate::timer::TimerComplementaryPin)), + (("timer", "CH3"), quote!(crate::timer::TimerPin)), + (("timer", "CH3N"), quote!(crate::timer::TimerComplementaryPin)), + (("timer", "CH4"), quote!(crate::timer::TimerPin)), + (("timer", "CH4N"), quote!(crate::timer::TimerComplementaryPin)), (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), - (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), - (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), - (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), - (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), - (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), - (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), + (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), + (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), + (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), + (("timer", "BKIN2"), quote!(crate::timer::BreakInputPin)), + (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), + (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), @@ -1475,10 +1475,10 @@ fn main() { (("hash", "IN"), quote!(crate::hash::Dma)), (("cryp", "IN"), quote!(crate::cryp::DmaIn)), (("cryp", "OUT"), quote!(crate::cryp::DmaOut)), - (("timer", "CH1"), quote!(crate::timer::Ch1Dma)), - (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), - (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), - (("timer", "CH4"), quote!(crate::timer::Ch4Dma)), + (("timer", "CH1"), quote!(crate::timer::Dma)), + (("timer", "CH2"), quote!(crate::timer::Dma)), + (("timer", "CH3"), quote!(crate::timer::Dma)), + (("timer", "CH4"), quote!(crate::timer::Dma)), (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver ] diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 8eec6c0c7..4600dd1a3 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -5,14 +5,12 @@ use core::marker::PhantomData; use stm32_metapac::timer::vals::Ckd; use super::low_level::{CountingMode, OutputPolarity, Timer}; -use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; -use super::{ - AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin, - Channel4ComplementaryPin, -}; +use super::simple_pwm::PwmPin; +use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, TimerChannel, TimerComplementaryPin}; use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::timer::low_level::OutputCompareMode; +use crate::timer::Channel; use crate::Peri; /// Complementary PWM pin wrapper. @@ -23,32 +21,23 @@ pub struct ComplementaryPwmPin<'d, T, C> { phantom: PhantomData<(T, C)>, } -macro_rules! complementary_channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { - #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>, output_type: OutputType) -> Self { - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), - ); - }); - ComplementaryPwmPin { - _pin: pin.into(), - phantom: PhantomData, - } - } +impl<'d, T: AdvancedInstance4Channel, C: Channel> ComplementaryPwmPin<'d, T, C> { + /// Create a new complementary PWM pin instance. + pub fn new(pin: Peri<'d, impl TimerComplementaryPin>, output_type: OutputType) -> Self { + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), + ); + }); + ComplementaryPwmPin { + _pin: pin.into(), + phantom: PhantomData, } - }; + } } -complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin); -complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin); -complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); -complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); - /// PWM driver with support for standard and complementary outputs. pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> { inner: Timer<'d, T>, @@ -82,24 +71,29 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { this.inner.enable_outputs(); - [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] - .iter() - .for_each(|&channel| { - this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); - this.inner.set_output_compare_preload(channel, true); - }); + [ + TimerChannel::Ch1, + TimerChannel::Ch2, + TimerChannel::Ch3, + TimerChannel::Ch4, + ] + .iter() + .for_each(|&channel| { + this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); + this.inner.set_output_compare_preload(channel, true); + }); this } /// Enable the given channel. - pub fn enable(&mut self, channel: Channel) { + pub fn enable(&mut self, channel: TimerChannel) { self.inner.enable_channel(channel, true); self.inner.enable_complementary_channel(channel, true); } /// Disable the given channel. - pub fn disable(&mut self, channel: Channel) { + pub fn disable(&mut self, channel: TimerChannel) { self.inner.enable_complementary_channel(channel, false); self.inner.enable_channel(channel, false); } @@ -127,13 +121,13 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn set_duty(&mut self, channel: Channel, duty: u16) { + pub fn set_duty(&mut self, channel: TimerChannel, duty: u16) { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty as _) } /// Set the output polarity for a given channel. - pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + pub fn set_polarity(&mut self, channel: TimerChannel, polarity: OutputPolarity) { self.inner.set_output_polarity(channel, polarity); self.inner.set_complementary_output_polarity(channel, polarity); } @@ -148,7 +142,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { } impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { - type Channel = Channel; + type Channel = TimerChannel; type Time = Hertz; type Duty = u16; diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index ec8b1ddf1..da567d504 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -6,14 +6,12 @@ use core::pin::Pin; use core::task::{Context, Poll}; use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; -use super::{ - CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, - GeneralInstance4Channel, -}; +use super::{CaptureCompareInterruptHandler, GeneralInstance4Channel, TimerChannel, TimerPin}; pub use super::{Ch1, Ch2, Ch3, Ch4}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::time::Hertz; +use crate::timer::Channel; use crate::Peri; /// Capture pin wrapper. @@ -23,27 +21,17 @@ pub struct CapturePin<'d, T, C> { _pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } - -macro_rules! channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { - #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - CapturePin { - _pin: pin.into(), - phantom: PhantomData, - } - } +impl<'d, T: GeneralInstance4Channel, C: Channel> CapturePin<'d, T, C> { + /// Create a new capture pin instance. + pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { + pin.set_as_af(pin.af_num(), AfType::input(pull)); + CapturePin { + _pin: pin.into(), + phantom: PhantomData, } - }; + } } -channel_impl!(new_ch1, Ch1, Channel1Pin); -channel_impl!(new_ch2, Ch2, Channel2Pin); -channel_impl!(new_ch3, Ch3, Channel3Pin); -channel_impl!(new_ch4, Ch4, Channel4Pin); - /// Input capture driver. pub struct InputCapture<'d, T: GeneralInstance4Channel> { inner: Timer<'d, T>, @@ -80,41 +68,46 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } /// Enable the given channel. - pub fn enable(&mut self, channel: Channel) { + pub fn enable(&mut self, channel: TimerChannel) { self.inner.enable_channel(channel, true); } /// Disable the given channel. - pub fn disable(&mut self, channel: Channel) { + pub fn disable(&mut self, channel: TimerChannel) { self.inner.enable_channel(channel, false); } /// Check whether given channel is enabled - pub fn is_enabled(&self, channel: Channel) -> bool { + pub fn is_enabled(&self, channel: TimerChannel) -> bool { self.inner.get_channel_enable_state(channel) } /// Set the input capture mode for a given channel. - pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { + pub fn set_input_capture_mode(&mut self, channel: TimerChannel, mode: InputCaptureMode) { self.inner.set_input_capture_mode(channel, mode); } /// Set input TI selection. - pub fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { + pub fn set_input_ti_selection(&mut self, channel: TimerChannel, tisel: InputTISelection) { self.inner.set_input_ti_selection(channel, tisel) } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: Channel) -> u32 { + pub fn get_capture_value(&self, channel: TimerChannel) -> u32 { self.inner.get_capture_value(channel) } /// Get input interrupt. - pub fn get_input_interrupt(&self, channel: Channel) -> bool { + pub fn get_input_interrupt(&self, channel: TimerChannel) -> bool { self.inner.get_input_interrupt(channel) } - fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { + fn new_future( + &self, + channel: TimerChannel, + mode: InputCaptureMode, + tisel: InputTISelection, + ) -> InputCaptureFuture { // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode self.inner.set_input_ti_selection(channel, tisel); @@ -131,37 +124,37 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } /// Asynchronously wait until the pin sees a rising edge. - pub async fn wait_for_rising_edge(&mut self, channel: Channel) -> u32 { + pub async fn wait_for_rising_edge(&mut self, channel: TimerChannel) -> u32 { self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Normal) .await } /// Asynchronously wait until the pin sees a falling edge. - pub async fn wait_for_falling_edge(&mut self, channel: Channel) -> u32 { + pub async fn wait_for_falling_edge(&mut self, channel: TimerChannel) -> u32 { self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Normal) .await } /// Asynchronously wait until the pin sees any edge. - pub async fn wait_for_any_edge(&mut self, channel: Channel) -> u32 { + pub async fn wait_for_any_edge(&mut self, channel: TimerChannel) -> u32 { self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Normal) .await } /// Asynchronously wait until the (alternate) pin sees a rising edge. - pub async fn wait_for_rising_edge_alternate(&mut self, channel: Channel) -> u32 { + pub async fn wait_for_rising_edge_alternate(&mut self, channel: TimerChannel) -> u32 { self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Alternate) .await } /// Asynchronously wait until the (alternate) pin sees a falling edge. - pub async fn wait_for_falling_edge_alternate(&mut self, channel: Channel) -> u32 { + pub async fn wait_for_falling_edge_alternate(&mut self, channel: TimerChannel) -> u32 { self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Alternate) .await } /// Asynchronously wait until the (alternate) pin sees any edge. - pub async fn wait_for_any_edge_alternate(&mut self, channel: Channel) -> u32 { + pub async fn wait_for_any_edge_alternate(&mut self, channel: TimerChannel) -> u32 { self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Alternate) .await } @@ -169,7 +162,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { #[must_use = "futures do nothing unless you `.await` or poll them"] struct InputCaptureFuture { - channel: Channel, + channel: TimerChannel, phantom: PhantomData, } diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index dc8ceb725..bfdbcf968 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -503,7 +503,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set input capture filter. - pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) { + pub fn set_input_capture_filter(&self, channel: TimerChannel, icf: vals::FilterValue) { let raw_channel = channel.index(); self.regs_gp16() .ccmr_input(raw_channel / 2) @@ -511,22 +511,22 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Clear input interrupt. - pub fn clear_input_interrupt(&self, channel: Channel) { + pub fn clear_input_interrupt(&self, channel: TimerChannel) { self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); } /// Get input interrupt. - pub fn get_input_interrupt(&self, channel: Channel) -> bool { + pub fn get_input_interrupt(&self, channel: TimerChannel) -> bool { self.regs_gp16().sr().read().ccif(channel.index()) } /// Enable input interrupt. - pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { + pub fn enable_input_interrupt(&self, channel: TimerChannel, enable: bool) { self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); } /// Set input capture prescaler. - pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) { + pub fn set_input_capture_prescaler(&self, channel: TimerChannel, factor: u8) { let raw_channel = channel.index(); self.regs_gp16() .ccmr_input(raw_channel / 2) @@ -534,7 +534,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set input TI selection. - pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) { + pub fn set_input_ti_selection(&self, channel: TimerChannel, tisel: InputTISelection) { let raw_channel = channel.index(); self.regs_gp16() .ccmr_input(raw_channel / 2) @@ -542,7 +542,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set input capture mode. - pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) { + pub fn set_input_capture_mode(&self, channel: TimerChannel, mode: InputCaptureMode) { self.regs_gp16().ccer().modify(|r| match mode { InputCaptureMode::Rising => { r.set_ccnp(channel.index(), false); @@ -560,7 +560,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set output compare mode. - pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { + pub fn set_output_compare_mode(&self, channel: TimerChannel, mode: OutputCompareMode) { let raw_channel: usize = channel.index(); self.regs_gp16() .ccmr_output(raw_channel / 2) @@ -568,24 +568,24 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set output polarity. - pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { + pub fn set_output_polarity(&self, channel: TimerChannel, polarity: OutputPolarity) { self.regs_gp16() .ccer() .modify(|w| w.set_ccp(channel.index(), polarity.into())); } /// Enable/disable a channel. - pub fn enable_channel(&self, channel: Channel, enable: bool) { + pub fn enable_channel(&self, channel: TimerChannel, enable: bool) { self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); } /// Get enable/disable state of a channel - pub fn get_channel_enable_state(&self, channel: Channel) -> bool { + pub fn get_channel_enable_state(&self, channel: TimerChannel) -> bool { self.regs_gp16().ccer().read().cce(channel.index()) } /// Set compare value for a channel. - pub fn set_compare_value(&self, channel: Channel, value: u32) { + pub fn set_compare_value(&self, channel: TimerChannel, value: u32) { match T::BITS { TimerBits::Bits16 => { let value = unwrap!(u16::try_from(value)); @@ -599,7 +599,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Get compare value for a channel. - pub fn get_compare_value(&self, channel: Channel) -> u32 { + pub fn get_compare_value(&self, channel: TimerChannel) -> u32 { match T::BITS { TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, #[cfg(not(stm32l0))] @@ -608,12 +608,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: Channel) -> u32 { + pub fn get_capture_value(&self, channel: TimerChannel) -> u32 { self.get_compare_value(channel) } /// Set output compare preload. - pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) { + pub fn set_output_compare_preload(&self, channel: TimerChannel, preload: bool) { let channel_index = channel.index(); self.regs_gp16() .ccmr_output(channel_index / 2) @@ -631,12 +631,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Get capture compare DMA enable state - pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { + pub fn get_cc_dma_enable_state(&self, channel: TimerChannel) -> bool { self.regs_gp16().dier().read().ccde(channel.index()) } /// Set capture compare DMA enable state - pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { + pub fn set_cc_dma_enable_state(&self, channel: TimerChannel, ccde: bool) { self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) } @@ -713,14 +713,14 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { } /// Set complementary output polarity. - pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { + pub fn set_complementary_output_polarity(&self, channel: TimerChannel, polarity: OutputPolarity) { self.regs_advanced() .ccer() .modify(|w| w.set_ccnp(channel.index(), polarity.into())); } /// Enable/disable a complementary channel. - pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) { + pub fn enable_complementary_channel(&self, channel: TimerChannel, enable: bool) { self.regs_advanced() .ccer() .modify(|w| w.set_ccne(channel.index(), enable)); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b29382fc8..362d95e25 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -19,7 +19,7 @@ use crate::rcc::RccPeripheral; /// Timer channel. #[derive(Clone, Copy)] -pub enum Channel { +pub enum TimerChannel { /// Channel 1. Ch1, /// Channel 2. @@ -30,14 +30,14 @@ pub enum Channel { Ch4, } -impl Channel { +impl TimerChannel { /// Get the channel index (0..3) pub fn index(&self) -> usize { match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, - Channel::Ch3 => 2, - Channel::Ch4 => 3, + TimerChannel::Ch1 => 0, + TimerChannel::Ch2 => 1, + TimerChannel::Ch3 => 2, + TimerChannel::Ch4 => 3, } } } @@ -51,6 +51,80 @@ pub enum Ch3 {} /// Channel 4 marker type. pub enum Ch4 {} +/// Timer channel trait. +#[allow(private_bounds)] +pub trait Channel: SealedChannel { + /// The runtime channel. + const CHANNEL: TimerChannel; +} + +trait SealedChannel {} + +impl Channel for Ch1 { + const CHANNEL: TimerChannel = TimerChannel::Ch1; +} + +impl Channel for Ch2 { + const CHANNEL: TimerChannel = TimerChannel::Ch2; +} + +impl Channel for Ch3 { + const CHANNEL: TimerChannel = TimerChannel::Ch3; +} + +impl Channel for Ch4 { + const CHANNEL: TimerChannel = TimerChannel::Ch4; +} + +impl SealedChannel for Ch1 {} +impl SealedChannel for Ch2 {} +impl SealedChannel for Ch3 {} +impl SealedChannel for Ch4 {} + +/// Timer break input. +#[derive(Clone, Copy)] +pub enum BkIn { + /// Break input 1. + BkIn1, + /// Break input 2. + BkIn2, +} + +impl BkIn { + /// Get the channel index (0..3) + pub fn index(&self) -> usize { + match self { + BkIn::BkIn1 => 0, + BkIn::BkIn2 => 1, + } + } +} + +/// Break input 1 marker type. +pub enum BkIn1 {} +/// Break input 2 marker type. +pub enum BkIn2 {} + +/// Timer channel trait. +#[allow(private_bounds)] +pub trait BreakInput: SealedBreakInput { + /// The runtim timer channel. + const INPUT: BkIn; +} + +trait SealedBreakInput {} + +impl BreakInput for BkIn1 { + const INPUT: BkIn = BkIn::BkIn1; +} + +impl BreakInput for BkIn2 { + const INPUT: BkIn = BkIn::BkIn2; +} + +impl SealedBreakInput for BkIn1 {} +impl SealedBreakInput for BkIn2 {} + /// Amount of bits of a timer. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -149,33 +223,20 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad /// Advanced 16-bit timer with 4 channels instance. pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} -pin_trait!(Channel1Pin, GeneralInstance4Channel); -pin_trait!(Channel2Pin, GeneralInstance4Channel); -pin_trait!(Channel3Pin, GeneralInstance4Channel); -pin_trait!(Channel4Pin, GeneralInstance4Channel); +pin_trait!(TimerPin, GeneralInstance4Channel, Channel); pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); -pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel); -pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel); -pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel); -pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel); - -pin_trait!(BreakInputPin, AdvancedInstance4Channel); -pin_trait!(BreakInput2Pin, AdvancedInstance4Channel); +pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, Channel); -pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel); -pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel); +pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput); -pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel); -pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel); +pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput); +pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput); // Update Event trigger DMA for every timer dma_trait!(UpDma, BasicInstance); -dma_trait!(Ch1Dma, GeneralInstance4Channel); -dma_trait!(Ch2Dma, GeneralInstance4Channel); -dma_trait!(Ch3Dma, GeneralInstance4Channel); -dma_trait!(Ch4Dma, GeneralInstance4Channel); +dma_trait!(Dma, GeneralInstance4Channel, Channel); #[allow(unused)] macro_rules! impl_core_timer { diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 933165ef9..47c1d7f49 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -9,9 +9,7 @@ use core::task::{Context, Poll}; use super::low_level::{ CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource, }; -use super::{ - CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel, -}; +use super::{CaptureCompareInterruptHandler, ExternalTriggerPin, GeneralInstance4Channel, TimerChannel, TimerPin}; pub use super::{Ch1, Ch2}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; @@ -48,24 +46,40 @@ pub struct TriggerPin<'d, T, C> { phantom: PhantomData<(T, C)>, } -macro_rules! channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> { - #[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - TriggerPin { - _pin: pin.into(), - phantom: PhantomData, - } - } +// TODO: Generify trigger inputs + +impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ch1> { + /// "Create a new Ch1 trigger pin instance. + pub fn new_ch1(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { + pin.set_as_af(pin.af_num(), AfType::input(pull)); + TriggerPin { + _pin: pin.into(), + phantom: PhantomData, } - }; + } } -channel_impl!(new_ch1, Ch1, Channel1Pin); -channel_impl!(new_ch2, Ch2, Channel2Pin); -channel_impl!(new_ext, Ext, ExternalTriggerPin); +impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ch2> { + /// "Create a new Ch2 trigger pin instance. + pub fn new_ch2(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { + pin.set_as_af(pin.af_num(), AfType::input(pull)); + TriggerPin { + _pin: pin.into(), + phantom: PhantomData, + } + } +} + +impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { + /// "Create a new EXT trigger pin instance. + pub fn new_ext(pin: Peri<'d, impl ExternalTriggerPin>, pull: Pull) -> Self { + pin.set_as_af(pin.af_num(), AfType::input(pull)); + TriggerPin { + _pin: pin.into(), + phantom: PhantomData, + } + } +} /// One pulse driver. /// @@ -91,9 +105,9 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { this.inner.set_trigger_source(TriggerSource::TI1F_ED); this.inner - .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); + .set_input_ti_selection(TimerChannel::Ch1, InputTISelection::Normal); this.inner - .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); + .set_input_capture_filter(TimerChannel::Ch1, FilterValue::NO_FILTER); this.new_inner(freq, pulse_end, counting_mode); this @@ -116,10 +130,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { this.inner.set_trigger_source(TriggerSource::TI1FP1); this.inner - .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); + .set_input_ti_selection(TimerChannel::Ch1, InputTISelection::Normal); this.inner - .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); - this.inner.set_input_capture_mode(Channel::Ch1, capture_mode); + .set_input_capture_filter(TimerChannel::Ch1, FilterValue::NO_FILTER); + this.inner.set_input_capture_mode(TimerChannel::Ch1, capture_mode); this.new_inner(freq, pulse_end, counting_mode); this @@ -142,10 +156,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { this.inner.set_trigger_source(TriggerSource::TI2FP2); this.inner - .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); + .set_input_ti_selection(TimerChannel::Ch2, InputTISelection::Normal); this.inner - .set_input_capture_filter(Channel::Ch2, FilterValue::NO_FILTER); - this.inner.set_input_capture_mode(Channel::Ch2, capture_mode); + .set_input_capture_filter(TimerChannel::Ch2, FilterValue::NO_FILTER); + this.inner.set_input_capture_mode(TimerChannel::Ch2, capture_mode); this.new_inner(freq, pulse_end, counting_mode); this @@ -217,7 +231,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// Get a single channel /// /// If you need to use multiple channels, use [`Self::split`]. - pub fn channel(&mut self, channel: Channel) -> OnePulseChannel<'_, T> { + pub fn channel(&mut self, channel: TimerChannel) -> OnePulseChannel<'_, T> { OnePulseChannel { inner: unsafe { self.inner.clone_unchecked() }, channel, @@ -230,7 +244,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch1(&mut self) -> OnePulseChannel<'_, T> { - self.channel(Channel::Ch1) + self.channel(TimerChannel::Ch1) } /// Channel 2 @@ -239,7 +253,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch2(&mut self) -> OnePulseChannel<'_, T> { - self.channel(Channel::Ch2) + self.channel(TimerChannel::Ch2) } /// Channel 3 @@ -248,7 +262,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch3(&mut self) -> OnePulseChannel<'_, T> { - self.channel(Channel::Ch3) + self.channel(TimerChannel::Ch3) } /// Channel 4 @@ -257,7 +271,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch4(&mut self) -> OnePulseChannel<'_, T> { - self.channel(Channel::Ch4) + self.channel(TimerChannel::Ch4) } /// Splits a [`OnePulse`] into four output channels. @@ -276,10 +290,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { }; OnePulseChannels { - ch1: ch(Channel::Ch1), - ch2: ch(Channel::Ch2), - ch3: ch(Channel::Ch3), - ch4: ch(Channel::Ch4), + ch1: ch(TimerChannel::Ch1), + ch2: ch(TimerChannel::Ch2), + ch3: ch(TimerChannel::Ch3), + ch4: ch(TimerChannel::Ch4), } } } @@ -303,7 +317,7 @@ pub struct OnePulseChannels<'d, T: GeneralInstance4Channel> { /// configuration is shared with all four channels. pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { inner: ManuallyDrop>, - channel: Channel, + channel: TimerChannel, } impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { @@ -350,7 +364,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { #[must_use = "futures do nothing unless you `.await` or poll them"] struct OnePulseFuture { - channel: Channel, + channel: TimerChannel, phantom: PhantomData, } diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 98b798634..3f9e5f651 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -1,33 +1,33 @@ //! PWM Input driver. use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; -use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; +use super::{Ch1, Ch2, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::gpio::{AfType, Pull}; use crate::time::Hertz; use crate::Peri; /// PWM Input driver. pub struct PwmInput<'d, T: GeneralInstance4Channel> { - channel: Channel, + channel: TimerChannel, inner: Timer<'d, T>, } impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Create a new PWM input driver. - pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl Channel1Pin>, pull: Pull, freq: Hertz) -> Self { + pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); - Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) + Self::new_inner(tim, freq, TimerChannel::Ch1, TimerChannel::Ch2) } /// Create a new PWM input driver. - pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl Channel2Pin>, pull: Pull, freq: Hertz) -> Self { + pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); - Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) + Self::new_inner(tim, freq, TimerChannel::Ch2, TimerChannel::Ch1) } - fn new_inner(tim: Peri<'d, T>, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { + fn new_inner(tim: Peri<'d, T>, freq: Hertz, ch1: TimerChannel, ch2: TimerChannel) -> Self { let mut inner = Timer::new(tim); inner.set_counting_mode(CountingMode::EdgeAlignedUp); @@ -44,8 +44,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { inner.set_input_capture_mode(ch2, InputCaptureMode::Falling); inner.set_trigger_source(match ch1 { - Channel::Ch1 => TriggerSource::TI1FP1, - Channel::Ch2 => TriggerSource::TI2FP2, + TimerChannel::Ch1 => TriggerSource::TI1FP1, + TimerChannel::Ch2 => TriggerSource::TI2FP2, _ => panic!("Invalid channel for PWM input"), }); @@ -58,19 +58,19 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Enable the given channel. pub fn enable(&mut self) { - self.inner.enable_channel(Channel::Ch1, true); - self.inner.enable_channel(Channel::Ch2, true); + self.inner.enable_channel(TimerChannel::Ch1, true); + self.inner.enable_channel(TimerChannel::Ch2, true); } /// Disable the given channel. pub fn disable(&mut self) { - self.inner.enable_channel(Channel::Ch1, false); - self.inner.enable_channel(Channel::Ch2, false); + self.inner.enable_channel(TimerChannel::Ch1, false); + self.inner.enable_channel(TimerChannel::Ch2, false); } /// Check whether given channel is enabled pub fn is_enabled(&self) -> bool { - self.inner.get_channel_enable_state(Channel::Ch1) + self.inner.get_channel_enable_state(TimerChannel::Ch1) } /// Get the period tick count @@ -81,8 +81,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Get the pulse width tick count pub fn get_width_ticks(&self) -> u32 { self.inner.get_capture_value(match self.channel { - Channel::Ch1 => Channel::Ch2, - Channel::Ch2 => Channel::Ch1, + TimerChannel::Ch1 => TimerChannel::Ch2, + TimerChannel::Ch2 => TimerChannel::Ch1, _ => panic!("Invalid channel for PWM input"), }) } diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index f3c81667c..bc7e71290 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -6,7 +6,7 @@ use stm32_metapac::timer::vals; use super::low_level::Timer; pub use super::{Ch1, Ch2}; -use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; +use super::{GeneralInstance4Channel, TimerPin}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::Peri; @@ -24,11 +24,13 @@ pub struct QeiPin<'d, T, Channel> { phantom: PhantomData<(T, Channel)>, } +// TODO: generify QEI channels + macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>) -> Self { + pub fn $new_chx(pin: Peri<'d, impl $pin_trait>) -> Self { critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); @@ -42,8 +44,8 @@ macro_rules! channel_impl { }; } -channel_impl!(new_ch1, Ch1, Channel1Pin); -channel_impl!(new_ch2, Ch2, Channel2Pin); +channel_impl!(new_ch1, Ch1, TimerPin); +channel_impl!(new_ch2, Ch2, TimerPin); /// Quadrature decoder driver. pub struct Qei<'d, T: GeneralInstance4Channel> { diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f7f433154..02835c379 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -4,22 +4,13 @@ use core::marker::PhantomData; use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; -use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, TimerBits}; +use super::{Ch1, Ch2, Ch3, Ch4, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::time::Hertz; use crate::Peri; -/// Channel 1 marker type. -pub enum Ch1 {} -/// Channel 2 marker type. -pub enum Ch2 {} -/// Channel 3 marker type. -pub enum Ch3 {} -/// Channel 4 marker type. -pub enum Ch4 {} - /// PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. @@ -47,7 +38,7 @@ macro_rules! channel_impl { ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>, output_type: OutputType) -> Self { + pub fn $new_chx(pin: Peri<'d, impl $pin_trait>, output_type: OutputType) -> Self { critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); @@ -59,7 +50,7 @@ macro_rules! channel_impl { } #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")] - pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait>, pin_config: PwmPinConfig) -> Self { + pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait>, pin_config: PwmPinConfig) -> Self { critical_section::with(|_| { pin.set_low(); pin.set_as_af( @@ -79,10 +70,10 @@ macro_rules! channel_impl { }; } -channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin); -channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin); -channel_impl!(new_ch3, new_ch3_with_config, Ch3, Channel3Pin); -channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin); +channel_impl!(new_ch1, new_ch1_with_config, Ch1, TimerPin); +channel_impl!(new_ch2, new_ch2_with_config, Ch2, TimerPin); +channel_impl!(new_ch3, new_ch3_with_config, Ch3, TimerPin); +channel_impl!(new_ch4, new_ch4_with_config, Ch4, TimerPin); /// A single channel of a pwm, obtained from [`SimplePwm::split`], /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. @@ -91,7 +82,7 @@ channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin); /// the frequency configuration is shared with all four channels. pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { timer: ManuallyDrop>, - channel: Channel, + channel: TimerChannel, } // TODO: check for RMW races @@ -216,13 +207,18 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details this.inner.start(); - [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] - .iter() - .for_each(|&channel| { - this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); + [ + TimerChannel::Ch1, + TimerChannel::Ch2, + TimerChannel::Ch3, + TimerChannel::Ch4, + ] + .iter() + .for_each(|&channel| { + this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); - this.inner.set_output_compare_preload(channel, true); - }); + this.inner.set_output_compare_preload(channel, true); + }); this } @@ -230,7 +226,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Get a single channel /// /// If you need to use multiple channels, use [`Self::split`]. - pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { + pub fn channel(&mut self, channel: TimerChannel) -> SimplePwmChannel<'_, T> { SimplePwmChannel { timer: unsafe { self.inner.clone_unchecked() }, channel, @@ -243,7 +239,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(Channel::Ch1) + self.channel(TimerChannel::Ch1) } /// Channel 2 @@ -252,7 +248,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(Channel::Ch2) + self.channel(TimerChannel::Ch2) } /// Channel 3 @@ -261,7 +257,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(Channel::Ch3) + self.channel(TimerChannel::Ch3) } /// Channel 4 @@ -270,7 +266,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(Channel::Ch4) + self.channel(TimerChannel::Ch4) } /// Splits a [`SimplePwm`] into four pwm channels. @@ -292,10 +288,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { }; SimplePwmChannels { - ch1: ch(Channel::Ch1), - ch2: ch(Channel::Ch2), - ch3: ch(Channel::Ch3), - ch4: ch(Channel::Ch4), + ch1: ch(TimerChannel::Ch1), + ch2: ch(TimerChannel::Ch2), + ch3: ch(TimerChannel::Ch3), + ch4: ch(TimerChannel::Ch4), } } @@ -326,7 +322,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: TimerChannel, duty: &[u16]) { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -409,8 +405,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { pub async fn waveform_up_multi_channel( &mut self, dma: Peri<'_, impl super::UpDma>, - starting_channel: Channel, - ending_channel: Channel, + starting_channel: TimerChannel, + ending_channel: TimerChannel, duty: &[u16], ) { let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; @@ -470,13 +466,13 @@ macro_rules! impl_waveform_chx { ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Generate a sequence of PWM waveform - pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch>, duty: &[u16]) { + pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch>, duty: &[u16]) { use crate::pac::timer::vals::Ccds; #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); - let cc_channel = Channel::$cc_ch; + let cc_channel = TimerChannel::$cc_ch; let original_duty_state = self.channel(cc_channel).current_duty_cycle(); let original_enable_state = self.channel(cc_channel).is_enabled(); @@ -562,10 +558,10 @@ macro_rules! impl_waveform_chx { }; } -impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1); -impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); -impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); -impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); +impl_waveform_chx!(waveform_ch1, Dma, Ch1); +impl_waveform_chx!(waveform_ch2, Dma, Ch2); +impl_waveform_chx!(waveform_ch3, Dma, Ch3); +impl_waveform_chx!(waveform_ch4, Dma, Ch4); impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { type Error = core::convert::Infallible; @@ -603,7 +599,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl } impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { - type Channel = Channel; + type Channel = TimerChannel; type Time = Hertz; type Duty = u32; -- cgit From 3bc2113651dc9c9b92fb789544f50aa296fba2e1 Mon Sep 17 00:00:00 2001 From: Nicholas Fasching <91689117+njfdev@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:32:09 -0400 Subject: Fix Release and Dev Profiles Being Backwards in rp235x Examples --- examples/rp235x/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index c81b79ae1..1346a75a2 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -57,9 +57,9 @@ portable-atomic = { version = "1.5", features = ["critical-section"] } log = "0.4" embedded-sdmmc = "0.7.0" -[profile.release] +[profile.dev] debug = 2 -[profile.dev] +[profile.release] lto = true opt-level = "z" -- cgit From 1623d4e639a54f21678381b54fd6d444309dab0a Mon Sep 17 00:00:00 2001 From: melvdl Date: Thu, 26 Jun 2025 22:55:12 +0200 Subject: stm32: generify timer::one_pulse and timer::qei pin constructors --- embassy-stm32/src/timer/one_pulse.rs | 88 ++++++++++++++++++++++++++---------- embassy-stm32/src/timer/qei.rs | 42 +++++++++-------- 2 files changed, 86 insertions(+), 44 deletions(-) diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 47c1d7f49..e89ad8390 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -7,7 +7,7 @@ use core::pin::Pin; use core::task::{Context, Poll}; use super::low_level::{ - CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource, + CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts, }; use super::{CaptureCompareInterruptHandler, ExternalTriggerPin, GeneralInstance4Channel, TimerChannel, TimerPin}; pub use super::{Ch1, Ch2}; @@ -46,33 +46,71 @@ pub struct TriggerPin<'d, T, C> { phantom: PhantomData<(T, C)>, } -// TODO: Generify trigger inputs +trait SealedTriggerSource {} -impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ch1> { - /// "Create a new Ch1 trigger pin instance. - pub fn new_ch1(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - TriggerPin { - _pin: pin.into(), - phantom: PhantomData, - } +/// Marker trait for a trigger source. +#[expect(private_bounds)] +pub trait TriggerSource: SealedTriggerSource {} + +impl TriggerSource for Ch1 {} +impl TriggerSource for Ch2 {} +impl TriggerSource for Ext {} + +impl SealedTriggerSource for Ch1 {} +impl SealedTriggerSource for Ch2 {} +impl SealedTriggerSource for Ext {} + +trait SealedTimerTriggerPin: crate::gpio::Pin {} + +/// Marker trait for a trigger pin. +#[expect(private_bounds)] +// TODO: find better naming scheme than prefixing all pin traits with "Timer". +// The trait name cannot conflict with the corresponding type's name. +// Applies to other timer submodules as well. +pub trait TimerTriggerPin: SealedTimerTriggerPin { + /// Get the AF number needed to use this pin as a trigger source. + fn af_num(&self) -> u8; +} + +impl TimerTriggerPin for P +where + T: GeneralInstance4Channel, + P: TimerPin, + C: super::Channel + TriggerSource, +{ + fn af_num(&self) -> u8 { + TimerPin::af_num(self) } } -impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ch2> { - /// "Create a new Ch2 trigger pin instance. - pub fn new_ch2(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - TriggerPin { - _pin: pin.into(), - phantom: PhantomData, - } +impl TimerTriggerPin for P +where + T: GeneralInstance4Channel, + P: ExternalTriggerPin, +{ + fn af_num(&self) -> u8 { + ExternalTriggerPin::af_num(self) } } -impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { - /// "Create a new EXT trigger pin instance. - pub fn new_ext(pin: Peri<'d, impl ExternalTriggerPin>, pull: Pull) -> Self { +impl SealedTimerTriggerPin for P +where + T: GeneralInstance4Channel, + P: TimerPin, + C: super::Channel + TriggerSource, +{ +} + +impl SealedTimerTriggerPin for P +where + T: GeneralInstance4Channel, + P: ExternalTriggerPin, +{ +} + +impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> { + /// "Create a new Ch1 trigger pin instance. + pub fn new(pin: Peri<'d, impl TimerTriggerPin>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); TriggerPin { _pin: pin.into(), @@ -103,7 +141,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { ) -> Self { let mut this = Self { inner: Timer::new(tim) }; - this.inner.set_trigger_source(TriggerSource::TI1F_ED); + this.inner.set_trigger_source(Ts::TI1F_ED); this.inner .set_input_ti_selection(TimerChannel::Ch1, InputTISelection::Normal); this.inner @@ -128,7 +166,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { ) -> Self { let mut this = Self { inner: Timer::new(tim) }; - this.inner.set_trigger_source(TriggerSource::TI1FP1); + this.inner.set_trigger_source(Ts::TI1FP1); this.inner .set_input_ti_selection(TimerChannel::Ch1, InputTISelection::Normal); this.inner @@ -154,7 +192,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { ) -> Self { let mut this = Self { inner: Timer::new(tim) }; - this.inner.set_trigger_source(TriggerSource::TI2FP2); + this.inner.set_trigger_source(Ts::TI2FP2); this.inner .set_input_ti_selection(TimerChannel::Ch2, InputTISelection::Normal); this.inner @@ -186,7 +224,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { // No filtering r.set_etf(FilterValue::NO_FILTER); }); - this.inner.set_trigger_source(TriggerSource::ETRF); + this.inner.set_trigger_source(Ts::ETRF); this.new_inner(freq, pulse_end, counting_mode); this diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index bc7e71290..657052cfa 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -8,6 +8,7 @@ use super::low_level::Timer; pub use super::{Ch1, Ch2}; use super::{GeneralInstance4Channel, TimerPin}; use crate::gpio::{AfType, AnyPin, Pull}; +use crate::timer::Channel; use crate::Peri; /// Counting direction @@ -24,28 +25,31 @@ pub struct QeiPin<'d, T, Channel> { phantom: PhantomData<(T, Channel)>, } -// TODO: generify QEI channels - -macro_rules! channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { - #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>) -> Self { - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); - }); - QeiPin { - _pin: pin.into(), - phantom: PhantomData, - } - } +impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { + /// Create a new QEI pin instance. + pub fn new(pin: Peri<'d, impl TimerPin>) -> Self { + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); + }); + QeiPin { + _pin: pin.into(), + phantom: PhantomData, } - }; + } } -channel_impl!(new_ch1, Ch1, TimerPin); -channel_impl!(new_ch2, Ch2, TimerPin); +trait SealedQeiChannel: Channel {} + +/// Marker trait for a timer channel eligible for use with QEI. +#[expect(private_bounds)] +pub trait QeiChannel: SealedQeiChannel {} + +impl QeiChannel for Ch1 {} +impl QeiChannel for Ch2 {} + +impl SealedQeiChannel for Ch1 {} +impl SealedQeiChannel for Ch2 {} /// Quadrature decoder driver. pub struct Qei<'d, T: GeneralInstance4Channel> { -- cgit From cbd24bf2eece65a787fc085c255e9b2932ea73e3 Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 01:04:47 +0200 Subject: stm32: fix timer break input 2 trait name in build script --- embassy-stm32/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 192688149..13fc23e54 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1103,8 +1103,8 @@ fn main() { (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), (("timer", "BKIN2"), quote!(crate::timer::BreakInputPin)), - (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), - (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), + (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), + (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), -- cgit From 6f88c2c73caa63a6e534130f4a064cb95d3e9d7d Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 01:08:28 +0200 Subject: stm32: rename timer channel trait; replace impls via macro with impls generic over timer channels --- embassy-stm32/src/timer/complementary_pwm.rs | 33 ++-- embassy-stm32/src/timer/input_capture.rs | 41 ++-- embassy-stm32/src/timer/low_level.rs | 38 ++-- embassy-stm32/src/timer/mod.rs | 48 ++--- embassy-stm32/src/timer/one_pulse.rs | 44 ++--- embassy-stm32/src/timer/pwm_input.rs | 28 +-- embassy-stm32/src/timer/qei.rs | 4 +- embassy-stm32/src/timer/simple_pwm.rs | 279 ++++++++++++--------------- 8 files changed, 241 insertions(+), 274 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 4600dd1a3..a450705a2 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -6,11 +6,11 @@ use stm32_metapac::timer::vals::Ckd; use super::low_level::{CountingMode, OutputPolarity, Timer}; use super::simple_pwm::PwmPin; -use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, TimerChannel, TimerComplementaryPin}; +use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::timer::low_level::OutputCompareMode; -use crate::timer::Channel; +use crate::timer::TimerChannel; use crate::Peri; /// Complementary PWM pin wrapper. @@ -21,7 +21,7 @@ pub struct ComplementaryPwmPin<'d, T, C> { phantom: PhantomData<(T, C)>, } -impl<'d, T: AdvancedInstance4Channel, C: Channel> ComplementaryPwmPin<'d, T, C> { +impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> { /// Create a new complementary PWM pin instance. pub fn new(pin: Peri<'d, impl TimerComplementaryPin>, output_type: OutputType) -> Self { critical_section::with(|_| { @@ -71,29 +71,24 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { this.inner.enable_outputs(); - [ - TimerChannel::Ch1, - TimerChannel::Ch2, - TimerChannel::Ch3, - TimerChannel::Ch4, - ] - .iter() - .for_each(|&channel| { - this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); - this.inner.set_output_compare_preload(channel, true); - }); + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .for_each(|&channel| { + this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); + this.inner.set_output_compare_preload(channel, true); + }); this } /// Enable the given channel. - pub fn enable(&mut self, channel: TimerChannel) { + pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); self.inner.enable_complementary_channel(channel, true); } /// Disable the given channel. - pub fn disable(&mut self, channel: TimerChannel) { + pub fn disable(&mut self, channel: Channel) { self.inner.enable_complementary_channel(channel, false); self.inner.enable_channel(channel, false); } @@ -121,13 +116,13 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn set_duty(&mut self, channel: TimerChannel, duty: u16) { + pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty as _) } /// Set the output polarity for a given channel. - pub fn set_polarity(&mut self, channel: TimerChannel, polarity: OutputPolarity) { + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { self.inner.set_output_polarity(channel, polarity); self.inner.set_complementary_output_polarity(channel, polarity); } @@ -142,7 +137,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { } impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { - type Channel = TimerChannel; + type Channel = Channel; type Time = Hertz; type Duty = u16; diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index da567d504..49e22b10f 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -6,12 +6,12 @@ use core::pin::Pin; use core::task::{Context, Poll}; use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; -use super::{CaptureCompareInterruptHandler, GeneralInstance4Channel, TimerChannel, TimerPin}; +use super::{CaptureCompareInterruptHandler, GeneralInstance4Channel, Channel, TimerPin}; pub use super::{Ch1, Ch2, Ch3, Ch4}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::time::Hertz; -use crate::timer::Channel; +use crate::timer::TimerChannel; use crate::Peri; /// Capture pin wrapper. @@ -21,7 +21,7 @@ pub struct CapturePin<'d, T, C> { _pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } -impl<'d, T: GeneralInstance4Channel, C: Channel> CapturePin<'d, T, C> { +impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { /// Create a new capture pin instance. pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); @@ -68,46 +68,41 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } /// Enable the given channel. - pub fn enable(&mut self, channel: TimerChannel) { + pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); } /// Disable the given channel. - pub fn disable(&mut self, channel: TimerChannel) { + pub fn disable(&mut self, channel: Channel) { self.inner.enable_channel(channel, false); } /// Check whether given channel is enabled - pub fn is_enabled(&self, channel: TimerChannel) -> bool { + pub fn is_enabled(&self, channel: Channel) -> bool { self.inner.get_channel_enable_state(channel) } /// Set the input capture mode for a given channel. - pub fn set_input_capture_mode(&mut self, channel: TimerChannel, mode: InputCaptureMode) { + pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { self.inner.set_input_capture_mode(channel, mode); } /// Set input TI selection. - pub fn set_input_ti_selection(&mut self, channel: TimerChannel, tisel: InputTISelection) { + pub fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { self.inner.set_input_ti_selection(channel, tisel) } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: TimerChannel) -> u32 { + pub fn get_capture_value(&self, channel: Channel) -> u32 { self.inner.get_capture_value(channel) } /// Get input interrupt. - pub fn get_input_interrupt(&self, channel: TimerChannel) -> bool { + pub fn get_input_interrupt(&self, channel: Channel) -> bool { self.inner.get_input_interrupt(channel) } - fn new_future( - &self, - channel: TimerChannel, - mode: InputCaptureMode, - tisel: InputTISelection, - ) -> InputCaptureFuture { + fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode self.inner.set_input_ti_selection(channel, tisel); @@ -124,37 +119,37 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } /// Asynchronously wait until the pin sees a rising edge. - pub async fn wait_for_rising_edge(&mut self, channel: TimerChannel) -> u32 { + pub async fn wait_for_rising_edge(&mut self, channel: Channel) -> u32 { self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Normal) .await } /// Asynchronously wait until the pin sees a falling edge. - pub async fn wait_for_falling_edge(&mut self, channel: TimerChannel) -> u32 { + pub async fn wait_for_falling_edge(&mut self, channel: Channel) -> u32 { self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Normal) .await } /// Asynchronously wait until the pin sees any edge. - pub async fn wait_for_any_edge(&mut self, channel: TimerChannel) -> u32 { + pub async fn wait_for_any_edge(&mut self, channel: Channel) -> u32 { self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Normal) .await } /// Asynchronously wait until the (alternate) pin sees a rising edge. - pub async fn wait_for_rising_edge_alternate(&mut self, channel: TimerChannel) -> u32 { + pub async fn wait_for_rising_edge_alternate(&mut self, channel: Channel) -> u32 { self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Alternate) .await } /// Asynchronously wait until the (alternate) pin sees a falling edge. - pub async fn wait_for_falling_edge_alternate(&mut self, channel: TimerChannel) -> u32 { + pub async fn wait_for_falling_edge_alternate(&mut self, channel: Channel) -> u32 { self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Alternate) .await } /// Asynchronously wait until the (alternate) pin sees any edge. - pub async fn wait_for_any_edge_alternate(&mut self, channel: TimerChannel) -> u32 { + pub async fn wait_for_any_edge_alternate(&mut self, channel: Channel) -> u32 { self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Alternate) .await } @@ -162,7 +157,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { #[must_use = "futures do nothing unless you `.await` or poll them"] struct InputCaptureFuture { - channel: TimerChannel, + channel: Channel, phantom: PhantomData, } diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index bfdbcf968..dc8ceb725 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -503,7 +503,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set input capture filter. - pub fn set_input_capture_filter(&self, channel: TimerChannel, icf: vals::FilterValue) { + pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) { let raw_channel = channel.index(); self.regs_gp16() .ccmr_input(raw_channel / 2) @@ -511,22 +511,22 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Clear input interrupt. - pub fn clear_input_interrupt(&self, channel: TimerChannel) { + pub fn clear_input_interrupt(&self, channel: Channel) { self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); } /// Get input interrupt. - pub fn get_input_interrupt(&self, channel: TimerChannel) -> bool { + pub fn get_input_interrupt(&self, channel: Channel) -> bool { self.regs_gp16().sr().read().ccif(channel.index()) } /// Enable input interrupt. - pub fn enable_input_interrupt(&self, channel: TimerChannel, enable: bool) { + pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); } /// Set input capture prescaler. - pub fn set_input_capture_prescaler(&self, channel: TimerChannel, factor: u8) { + pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) { let raw_channel = channel.index(); self.regs_gp16() .ccmr_input(raw_channel / 2) @@ -534,7 +534,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set input TI selection. - pub fn set_input_ti_selection(&self, channel: TimerChannel, tisel: InputTISelection) { + pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) { let raw_channel = channel.index(); self.regs_gp16() .ccmr_input(raw_channel / 2) @@ -542,7 +542,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set input capture mode. - pub fn set_input_capture_mode(&self, channel: TimerChannel, mode: InputCaptureMode) { + pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) { self.regs_gp16().ccer().modify(|r| match mode { InputCaptureMode::Rising => { r.set_ccnp(channel.index(), false); @@ -560,7 +560,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set output compare mode. - pub fn set_output_compare_mode(&self, channel: TimerChannel, mode: OutputCompareMode) { + pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { let raw_channel: usize = channel.index(); self.regs_gp16() .ccmr_output(raw_channel / 2) @@ -568,24 +568,24 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set output polarity. - pub fn set_output_polarity(&self, channel: TimerChannel, polarity: OutputPolarity) { + pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { self.regs_gp16() .ccer() .modify(|w| w.set_ccp(channel.index(), polarity.into())); } /// Enable/disable a channel. - pub fn enable_channel(&self, channel: TimerChannel, enable: bool) { + pub fn enable_channel(&self, channel: Channel, enable: bool) { self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); } /// Get enable/disable state of a channel - pub fn get_channel_enable_state(&self, channel: TimerChannel) -> bool { + pub fn get_channel_enable_state(&self, channel: Channel) -> bool { self.regs_gp16().ccer().read().cce(channel.index()) } /// Set compare value for a channel. - pub fn set_compare_value(&self, channel: TimerChannel, value: u32) { + pub fn set_compare_value(&self, channel: Channel, value: u32) { match T::BITS { TimerBits::Bits16 => { let value = unwrap!(u16::try_from(value)); @@ -599,7 +599,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Get compare value for a channel. - pub fn get_compare_value(&self, channel: TimerChannel) -> u32 { + pub fn get_compare_value(&self, channel: Channel) -> u32 { match T::BITS { TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, #[cfg(not(stm32l0))] @@ -608,12 +608,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: TimerChannel) -> u32 { + pub fn get_capture_value(&self, channel: Channel) -> u32 { self.get_compare_value(channel) } /// Set output compare preload. - pub fn set_output_compare_preload(&self, channel: TimerChannel, preload: bool) { + pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) { let channel_index = channel.index(); self.regs_gp16() .ccmr_output(channel_index / 2) @@ -631,12 +631,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Get capture compare DMA enable state - pub fn get_cc_dma_enable_state(&self, channel: TimerChannel) -> bool { + pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { self.regs_gp16().dier().read().ccde(channel.index()) } /// Set capture compare DMA enable state - pub fn set_cc_dma_enable_state(&self, channel: TimerChannel, ccde: bool) { + pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) } @@ -713,14 +713,14 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { } /// Set complementary output polarity. - pub fn set_complementary_output_polarity(&self, channel: TimerChannel, polarity: OutputPolarity) { + pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { self.regs_advanced() .ccer() .modify(|w| w.set_ccnp(channel.index(), polarity.into())); } /// Enable/disable a complementary channel. - pub fn enable_complementary_channel(&self, channel: TimerChannel, enable: bool) { + pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) { self.regs_advanced() .ccer() .modify(|w| w.set_ccne(channel.index(), enable)); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 362d95e25..7062f5f4c 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -19,7 +19,7 @@ use crate::rcc::RccPeripheral; /// Timer channel. #[derive(Clone, Copy)] -pub enum TimerChannel { +pub enum Channel { /// Channel 1. Ch1, /// Channel 2. @@ -30,14 +30,14 @@ pub enum TimerChannel { Ch4, } -impl TimerChannel { +impl Channel { /// Get the channel index (0..3) pub fn index(&self) -> usize { match self { - TimerChannel::Ch1 => 0, - TimerChannel::Ch2 => 1, - TimerChannel::Ch3 => 2, - TimerChannel::Ch4 => 3, + Channel::Ch1 => 0, + Channel::Ch2 => 1, + Channel::Ch3 => 2, + Channel::Ch4 => 3, } } } @@ -53,33 +53,33 @@ pub enum Ch4 {} /// Timer channel trait. #[allow(private_bounds)] -pub trait Channel: SealedChannel { +pub trait TimerChannel: SealedTimerChannel { /// The runtime channel. - const CHANNEL: TimerChannel; + const CHANNEL: Channel; } -trait SealedChannel {} +trait SealedTimerChannel {} -impl Channel for Ch1 { - const CHANNEL: TimerChannel = TimerChannel::Ch1; +impl TimerChannel for Ch1 { + const CHANNEL: Channel = Channel::Ch1; } -impl Channel for Ch2 { - const CHANNEL: TimerChannel = TimerChannel::Ch2; +impl TimerChannel for Ch2 { + const CHANNEL: Channel = Channel::Ch2; } -impl Channel for Ch3 { - const CHANNEL: TimerChannel = TimerChannel::Ch3; +impl TimerChannel for Ch3 { + const CHANNEL: Channel = Channel::Ch3; } -impl Channel for Ch4 { - const CHANNEL: TimerChannel = TimerChannel::Ch4; +impl TimerChannel for Ch4 { + const CHANNEL: Channel = Channel::Ch4; } -impl SealedChannel for Ch1 {} -impl SealedChannel for Ch2 {} -impl SealedChannel for Ch3 {} -impl SealedChannel for Ch4 {} +impl SealedTimerChannel for Ch1 {} +impl SealedTimerChannel for Ch2 {} +impl SealedTimerChannel for Ch3 {} +impl SealedTimerChannel for Ch4 {} /// Timer break input. #[derive(Clone, Copy)] @@ -223,10 +223,10 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad /// Advanced 16-bit timer with 4 channels instance. pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} -pin_trait!(TimerPin, GeneralInstance4Channel, Channel); +pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel); pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); -pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, Channel); +pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel); pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput); @@ -236,7 +236,7 @@ pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput); // Update Event trigger DMA for every timer dma_trait!(UpDma, BasicInstance); -dma_trait!(Dma, GeneralInstance4Channel, Channel); +dma_trait!(Dma, GeneralInstance4Channel, TimerChannel); #[allow(unused)] macro_rules! impl_core_timer { diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index e89ad8390..c8f0cafe7 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -9,7 +9,7 @@ use core::task::{Context, Poll}; use super::low_level::{ CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts, }; -use super::{CaptureCompareInterruptHandler, ExternalTriggerPin, GeneralInstance4Channel, TimerChannel, TimerPin}; +use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; pub use super::{Ch1, Ch2}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; @@ -76,7 +76,7 @@ impl TimerTriggerPin for P where T: GeneralInstance4Channel, P: TimerPin, - C: super::Channel + TriggerSource, + C: super::TimerChannel + TriggerSource, { fn af_num(&self) -> u8 { TimerPin::af_num(self) @@ -97,7 +97,7 @@ impl SealedTimerTriggerPin for P where T: GeneralInstance4Channel, P: TimerPin, - C: super::Channel + TriggerSource, + C: super::TimerChannel + TriggerSource, { } @@ -143,9 +143,9 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { this.inner.set_trigger_source(Ts::TI1F_ED); this.inner - .set_input_ti_selection(TimerChannel::Ch1, InputTISelection::Normal); + .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); this.inner - .set_input_capture_filter(TimerChannel::Ch1, FilterValue::NO_FILTER); + .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); this.new_inner(freq, pulse_end, counting_mode); this @@ -168,10 +168,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { this.inner.set_trigger_source(Ts::TI1FP1); this.inner - .set_input_ti_selection(TimerChannel::Ch1, InputTISelection::Normal); + .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); this.inner - .set_input_capture_filter(TimerChannel::Ch1, FilterValue::NO_FILTER); - this.inner.set_input_capture_mode(TimerChannel::Ch1, capture_mode); + .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); + this.inner.set_input_capture_mode(Channel::Ch1, capture_mode); this.new_inner(freq, pulse_end, counting_mode); this @@ -194,10 +194,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { this.inner.set_trigger_source(Ts::TI2FP2); this.inner - .set_input_ti_selection(TimerChannel::Ch2, InputTISelection::Normal); + .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); this.inner - .set_input_capture_filter(TimerChannel::Ch2, FilterValue::NO_FILTER); - this.inner.set_input_capture_mode(TimerChannel::Ch2, capture_mode); + .set_input_capture_filter(Channel::Ch2, FilterValue::NO_FILTER); + this.inner.set_input_capture_mode(Channel::Ch2, capture_mode); this.new_inner(freq, pulse_end, counting_mode); this @@ -269,7 +269,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// Get a single channel /// /// If you need to use multiple channels, use [`Self::split`]. - pub fn channel(&mut self, channel: TimerChannel) -> OnePulseChannel<'_, T> { + pub fn channel(&mut self, channel: Channel) -> OnePulseChannel<'_, T> { OnePulseChannel { inner: unsafe { self.inner.clone_unchecked() }, channel, @@ -282,7 +282,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch1(&mut self) -> OnePulseChannel<'_, T> { - self.channel(TimerChannel::Ch1) + self.channel(Channel::Ch1) } /// Channel 2 @@ -291,7 +291,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch2(&mut self) -> OnePulseChannel<'_, T> { - self.channel(TimerChannel::Ch2) + self.channel(Channel::Ch2) } /// Channel 3 @@ -300,7 +300,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch3(&mut self) -> OnePulseChannel<'_, T> { - self.channel(TimerChannel::Ch3) + self.channel(Channel::Ch3) } /// Channel 4 @@ -309,7 +309,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch4(&mut self) -> OnePulseChannel<'_, T> { - self.channel(TimerChannel::Ch4) + self.channel(Channel::Ch4) } /// Splits a [`OnePulse`] into four output channels. @@ -328,10 +328,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { }; OnePulseChannels { - ch1: ch(TimerChannel::Ch1), - ch2: ch(TimerChannel::Ch2), - ch3: ch(TimerChannel::Ch3), - ch4: ch(TimerChannel::Ch4), + ch1: ch(Channel::Ch1), + ch2: ch(Channel::Ch2), + ch3: ch(Channel::Ch3), + ch4: ch(Channel::Ch4), } } } @@ -355,7 +355,7 @@ pub struct OnePulseChannels<'d, T: GeneralInstance4Channel> { /// configuration is shared with all four channels. pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { inner: ManuallyDrop>, - channel: TimerChannel, + channel: Channel, } impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { @@ -402,7 +402,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { #[must_use = "futures do nothing unless you `.await` or poll them"] struct OnePulseFuture { - channel: TimerChannel, + channel: Channel, phantom: PhantomData, } diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 3f9e5f651..2e05a0593 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -1,14 +1,14 @@ //! PWM Input driver. use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; -use super::{Ch1, Ch2, GeneralInstance4Channel, TimerChannel, TimerPin}; +use super::{Ch1, Ch2, GeneralInstance4Channel, Channel, TimerPin}; use crate::gpio::{AfType, Pull}; use crate::time::Hertz; use crate::Peri; /// PWM Input driver. pub struct PwmInput<'d, T: GeneralInstance4Channel> { - channel: TimerChannel, + channel: Channel, inner: Timer<'d, T>, } @@ -17,17 +17,17 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); - Self::new_inner(tim, freq, TimerChannel::Ch1, TimerChannel::Ch2) + Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } /// Create a new PWM input driver. pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); - Self::new_inner(tim, freq, TimerChannel::Ch2, TimerChannel::Ch1) + Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) } - fn new_inner(tim: Peri<'d, T>, freq: Hertz, ch1: TimerChannel, ch2: TimerChannel) -> Self { + fn new_inner(tim: Peri<'d, T>, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { let mut inner = Timer::new(tim); inner.set_counting_mode(CountingMode::EdgeAlignedUp); @@ -44,8 +44,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { inner.set_input_capture_mode(ch2, InputCaptureMode::Falling); inner.set_trigger_source(match ch1 { - TimerChannel::Ch1 => TriggerSource::TI1FP1, - TimerChannel::Ch2 => TriggerSource::TI2FP2, + Channel::Ch1 => TriggerSource::TI1FP1, + Channel::Ch2 => TriggerSource::TI2FP2, _ => panic!("Invalid channel for PWM input"), }); @@ -58,19 +58,19 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Enable the given channel. pub fn enable(&mut self) { - self.inner.enable_channel(TimerChannel::Ch1, true); - self.inner.enable_channel(TimerChannel::Ch2, true); + self.inner.enable_channel(Channel::Ch1, true); + self.inner.enable_channel(Channel::Ch2, true); } /// Disable the given channel. pub fn disable(&mut self) { - self.inner.enable_channel(TimerChannel::Ch1, false); - self.inner.enable_channel(TimerChannel::Ch2, false); + self.inner.enable_channel(Channel::Ch1, false); + self.inner.enable_channel(Channel::Ch2, false); } /// Check whether given channel is enabled pub fn is_enabled(&self) -> bool { - self.inner.get_channel_enable_state(TimerChannel::Ch1) + self.inner.get_channel_enable_state(Channel::Ch1) } /// Get the period tick count @@ -81,8 +81,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Get the pulse width tick count pub fn get_width_ticks(&self) -> u32 { self.inner.get_capture_value(match self.channel { - TimerChannel::Ch1 => TimerChannel::Ch2, - TimerChannel::Ch2 => TimerChannel::Ch1, + Channel::Ch1 => Channel::Ch2, + Channel::Ch2 => Channel::Ch1, _ => panic!("Invalid channel for PWM input"), }) } diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 657052cfa..eabe1b22a 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -8,7 +8,7 @@ use super::low_level::Timer; pub use super::{Ch1, Ch2}; use super::{GeneralInstance4Channel, TimerPin}; use crate::gpio::{AfType, AnyPin, Pull}; -use crate::timer::Channel; +use crate::timer::TimerChannel; use crate::Peri; /// Counting direction @@ -39,7 +39,7 @@ impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { } } -trait SealedQeiChannel: Channel {} +trait SealedQeiChannel: TimerChannel {} /// Marker trait for a timer channel eligible for use with QEI. #[expect(private_bounds)] diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 02835c379..c04b1ab97 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; -use super::{Ch1, Ch2, Ch3, Ch4, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; +use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; @@ -34,46 +34,37 @@ pub struct PwmPinConfig { pub pull: Pull, } -macro_rules! channel_impl { - ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { - #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] - pub fn $new_chx(pin: Peri<'d, impl $pin_trait>, output_type: OutputType) -> Self { - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); - }); - PwmPin { - _pin: pin.into(), - phantom: PhantomData, - } - } - - #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")] - pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait>, pin_config: PwmPinConfig) -> Self { - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - #[cfg(gpio_v1)] - AfType::output(pin_config.output_type, pin_config.speed), - #[cfg(gpio_v2)] - AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), - ); - }); - PwmPin { - _pin: pin.into(), - phantom: PhantomData, - } - } +impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { + /// Create a new PWM pin instance. + pub fn new(pin: Peri<'d, impl TimerPin>, output_type: OutputType) -> Self { + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); + }); + PwmPin { + _pin: pin.into(), + phantom: PhantomData, } - }; -} + } -channel_impl!(new_ch1, new_ch1_with_config, Ch1, TimerPin); -channel_impl!(new_ch2, new_ch2_with_config, Ch2, TimerPin); -channel_impl!(new_ch3, new_ch3_with_config, Ch3, TimerPin); -channel_impl!(new_ch4, new_ch4_with_config, Ch4, TimerPin); + /// Create a new PWM pin instance with config. + pub fn new_with_config(pin: Peri<'d, impl TimerPin>, pin_config: PwmPinConfig) -> Self { + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + #[cfg(gpio_v1)] + AfType::output(pin_config.output_type, pin_config.speed), + #[cfg(gpio_v2)] + AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), + ); + }); + PwmPin { + _pin: pin.into(), + phantom: PhantomData, + } + } +} /// A single channel of a pwm, obtained from [`SimplePwm::split`], /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. @@ -82,7 +73,7 @@ channel_impl!(new_ch4, new_ch4_with_config, Ch4, TimerPin); /// the frequency configuration is shared with all four channels. pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { timer: ManuallyDrop>, - channel: TimerChannel, + channel: Channel, } // TODO: check for RMW races @@ -207,18 +198,13 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details this.inner.start(); - [ - TimerChannel::Ch1, - TimerChannel::Ch2, - TimerChannel::Ch3, - TimerChannel::Ch4, - ] - .iter() - .for_each(|&channel| { - this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); - - this.inner.set_output_compare_preload(channel, true); - }); + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .for_each(|&channel| { + this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); + + this.inner.set_output_compare_preload(channel, true); + }); this } @@ -226,7 +212,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Get a single channel /// /// If you need to use multiple channels, use [`Self::split`]. - pub fn channel(&mut self, channel: TimerChannel) -> SimplePwmChannel<'_, T> { + pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { SimplePwmChannel { timer: unsafe { self.inner.clone_unchecked() }, channel, @@ -239,7 +225,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(TimerChannel::Ch1) + self.channel(Channel::Ch1) } /// Channel 2 @@ -248,7 +234,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(TimerChannel::Ch2) + self.channel(Channel::Ch2) } /// Channel 3 @@ -257,7 +243,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(TimerChannel::Ch3) + self.channel(Channel::Ch3) } /// Channel 4 @@ -266,7 +252,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { - self.channel(TimerChannel::Ch4) + self.channel(Channel::Ch4) } /// Splits a [`SimplePwm`] into four pwm channels. @@ -288,10 +274,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { }; SimplePwmChannels { - ch1: ch(TimerChannel::Ch1), - ch2: ch(TimerChannel::Ch2), - ch3: ch(TimerChannel::Ch3), - ch4: ch(TimerChannel::Ch4), + ch1: ch(Channel::Ch1), + ch2: ch(Channel::Ch2), + ch3: ch(Channel::Ch3), + ch4: ch(Channel::Ch4), } } @@ -322,7 +308,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: TimerChannel, duty: &[u16]) { + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -405,8 +391,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { pub async fn waveform_up_multi_channel( &mut self, dma: Peri<'_, impl super::UpDma>, - starting_channel: TimerChannel, - ending_channel: TimerChannel, + starting_channel: Channel, + ending_channel: Channel, duty: &[u16], ) { let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; @@ -462,106 +448,97 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { } } -macro_rules! impl_waveform_chx { - ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { - impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { - /// Generate a sequence of PWM waveform - pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch>, duty: &[u16]) { - use crate::pac::timer::vals::Ccds; +impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { + /// Generate a sequence of PWM waveform + pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { + use crate::pac::timer::vals::Ccds; - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); - let cc_channel = TimerChannel::$cc_ch; + let cc_channel = C::CHANNEL; - let original_duty_state = self.channel(cc_channel).current_duty_cycle(); - let original_enable_state = self.channel(cc_channel).is_enabled(); - let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; - let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); + let original_duty_state = self.channel(cc_channel).current_duty_cycle(); + let original_enable_state = self.channel(cc_channel).is_enabled(); + let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; + let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); - // redirect CC DMA request onto Update Event - if !original_cc_dma_on_update { - self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) - } + // redirect CC DMA request onto Update Event + if !original_cc_dma_on_update { + self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) + } - if !original_cc_dma_enabled { - self.inner.set_cc_dma_enable_state(cc_channel, true); - } + if !original_cc_dma_enabled { + self.inner.set_cc_dma_enable_state(cc_channel, true); + } - if !original_enable_state { - self.channel(cc_channel).enable(); - } + if !original_enable_state { + self.channel(cc_channel).enable(); + } - unsafe { + unsafe { + #[cfg(not(any(bdma, gpdma)))] + use crate::dma::{Burst, FifoThreshold}; + use crate::dma::{Transfer, TransferOptions}; + + let dma_transfer_option = TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: Burst::Incr8, + ..Default::default() + }; + + match self.inner.bits() { + TimerBits::Bits16 => { + Transfer::new_write( + dma, + req, + duty, + self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, + dma_transfer_option, + ) + .await + } + #[cfg(not(any(stm32l0)))] + TimerBits::Bits32 => { #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, - ..Default::default() - }; - - match self.inner.bits() { - TimerBits::Bits16 => { - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ) - .await - } - #[cfg(not(any(stm32l0)))] - TimerBits::Bits32 => { - #[cfg(not(any(bdma, gpdma)))] - panic!("unsupported timer bits"); - - #[cfg(any(bdma, gpdma))] - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, - dma_transfer_option, - ) - .await - } - }; - }; - - // restore output compare state - if !original_enable_state { - self.channel(cc_channel).disable(); + panic!("unsupported timer bits"); + + #[cfg(any(bdma, gpdma))] + Transfer::new_write( + dma, + req, + duty, + self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, + dma_transfer_option, + ) + .await } + }; + }; - self.channel(cc_channel).set_duty_cycle(original_duty_state); + // restore output compare state + if !original_enable_state { + self.channel(cc_channel).disable(); + } - // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, - // this can almost always trigger a DMA FIFO error. - // - // optional TODO: - // clean FEIF after disable UDE - if !original_cc_dma_enabled { - self.inner.set_cc_dma_enable_state(cc_channel, false); - } + self.channel(cc_channel).set_duty_cycle(original_duty_state); - if !original_cc_dma_on_update { - self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) - } - } + // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, + // this can almost always trigger a DMA FIFO error. + // + // optional TODO: + // clean FEIF after disable UDE + if !original_cc_dma_enabled { + self.inner.set_cc_dma_enable_state(cc_channel, false); } - }; -} -impl_waveform_chx!(waveform_ch1, Dma, Ch1); -impl_waveform_chx!(waveform_ch2, Dma, Ch2); -impl_waveform_chx!(waveform_ch3, Dma, Ch3); -impl_waveform_chx!(waveform_ch4, Dma, Ch4); + if !original_cc_dma_on_update { + self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) + } + } +} impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { type Error = core::convert::Infallible; @@ -599,7 +576,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl } impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { - type Channel = TimerChannel; + type Channel = Channel; type Time = Hertz; type Duty = u32; -- cgit From 41327c1325367177548dabc644636617274de7f4 Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 01:08:45 +0200 Subject: stm32: adapt examples to timer API changes --- examples/stm32f1/src/bin/input_capture.rs | 2 +- examples/stm32f4/src/bin/input_capture.rs | 2 +- examples/stm32f4/src/bin/pwm.rs | 2 +- examples/stm32f4/src/bin/pwm_complementary.rs | 4 ++-- examples/stm32f4/src/bin/ws2812_pwm.rs | 2 +- examples/stm32g0/src/bin/hf_timer.rs | 4 ++-- examples/stm32g0/src/bin/input_capture.rs | 4 ++-- examples/stm32g0/src/bin/pwm_complementary.rs | 8 ++++---- examples/stm32g0/src/bin/pwm_input.rs | 2 +- examples/stm32g4/src/bin/pwm.rs | 2 +- examples/stm32h7/src/bin/low_level_timer_api.rs | 10 +++++----- examples/stm32h7/src/bin/pwm.rs | 2 +- examples/stm32l0/src/bin/dds.rs | 4 ++-- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index 6fe8e0b50..84811fb95 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PC13))); - let ch3 = CapturePin::new_ch3(p.PA2, Pull::None); + let ch3 = CapturePin::new(p.PA2, Pull::None); let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs index fe5e2bdfc..e15b4d26e 100644 --- a/examples/stm32f4/src/bin/input_capture.rs +++ b/examples/stm32f4/src/bin/input_capture.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB2))); - let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); + let ch3 = CapturePin::new(p.PB10, Pull::None); let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 04811162b..e385842f0 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull); + let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); let mut ch1 = pwm.ch1(); ch1.enable(); diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 161f43c48..c981f1a76 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -16,8 +16,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); - let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); + let ch1 = PwmPin::new(p.PE9, OutputType::PushPull); + let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull); let mut pwm = ComplementaryPwm::new( p.TIM1, Some(ch1), diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index ca924e181..5153e1cfd 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) { let mut ws2812_pwm = SimplePwm::new( dp.TIM3, - Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)), + Some(PwmPin::new(dp.PB4, OutputType::PushPull)), None, None, None, diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs index dfb6e0edc..705905f01 100644 --- a/examples/stm32g0/src/bin/hf_timer.rs +++ b/examples/stm32g0/src/bin/hf_timer.rs @@ -37,8 +37,8 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); - let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); + let ch1 = PwmPin::new(p.PA8, OutputType::PushPull); + let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull); let mut pwm = ComplementaryPwm::new( p.TIM1, diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 08df4e043..df339d541 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs @@ -47,12 +47,12 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB1))); // Connect PB1 and PA8 with a 1k Ohm resistor - let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); + let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); pwm.ch1().enable(); pwm.ch1().set_duty_cycle(50); - let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); + let ch1 = CapturePin::new(p.PA0, Pull::None); let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); let mut old_capture = 0; diff --git a/examples/stm32g0/src/bin/pwm_complementary.rs b/examples/stm32g0/src/bin/pwm_complementary.rs index 97b163c40..dbd9194c9 100644 --- a/examples/stm32g0/src/bin/pwm_complementary.rs +++ b/examples/stm32g0/src/bin/pwm_complementary.rs @@ -26,10 +26,10 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); - let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); - let ch2 = PwmPin::new_ch2(p.PB3, OutputType::PushPull); - let ch2n = ComplementaryPwmPin::new_ch2(p.PB0, OutputType::PushPull); + let ch1 = PwmPin::new(p.PA8, OutputType::PushPull); + let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull); + let ch2 = PwmPin::new(p.PB3, OutputType::PushPull); + let ch2n = ComplementaryPwmPin::new(p.PB0, OutputType::PushPull); let mut pwm = ComplementaryPwm::new( p.TIM1, diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 9d6b5fe97..dc2428607 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -42,7 +42,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB1))); // Connect PA8 and PA6 with a 1k Ohm resistor - let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); + let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); pwm.ch1().set_duty_cycle_fraction(1, 4); pwm.ch1().enable(); diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 6c965012c..5b4194927 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull); + let ch1_pin = PwmPin::new(p.PC0, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); let mut ch1 = pwm.ch1(); ch1.enable(); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 8de31ea5b..12abb8693 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed}; use embassy_stm32::time::{khz, Hertz}; use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; -use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; +use embassy_stm32::timer::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance32bit4Channel, TimerPin}; use embassy_stm32::{Config, Peri}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -67,10 +67,10 @@ pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> { impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { pub fn new( tim: Peri<'d, T>, - ch1: Peri<'d, impl Channel1Pin>, - ch2: Peri<'d, impl Channel2Pin>, - ch3: Peri<'d, impl Channel3Pin>, - ch4: Peri<'d, impl Channel4Pin>, + ch1: Peri<'d, impl TimerPin>, + ch2: Peri<'d, impl TimerPin>, + ch3: Peri<'d, impl TimerPin>, + ch4: Peri<'d, impl TimerPin>, freq: Hertz, ) -> Self { let af1 = ch1.af_num(); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index a1c53fc3f..73b43be69 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); + let ch1_pin = PwmPin::new(p.PA6, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); let mut ch1 = pwm.ch1(); ch1.enable(); diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs index a54b28a93..eaa7a61a8 100644 --- a/examples/stm32l0/src/bin/dds.rs +++ b/examples/stm32l0/src/bin/dds.rs @@ -11,7 +11,7 @@ use embassy_stm32::rcc::*; use embassy_stm32::time::hz; use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::timer::Channel; +use embassy_stm32::timer::{Ch3, Channel}; use embassy_stm32::{interrupt, pac, Config}; use panic_probe as _; @@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // setup PWM pin in AF mode - let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull); + let _ch3 = PwmPin::<_, Ch3>::new(p.PA2, OutputType::PushPull); // initialize timer // we cannot use SimplePWM here because the Time is privately encapsulated -- cgit From 2727fb266fa7d87b4fa23f7278e5255ca260fd14 Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 01:28:47 +0200 Subject: run cargo fmt --- embassy-stm32/src/timer/input_capture.rs | 2 +- embassy-stm32/src/timer/pwm_input.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 49e22b10f..dda33e7f1 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -6,7 +6,7 @@ use core::pin::Pin; use core::task::{Context, Poll}; use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; -use super::{CaptureCompareInterruptHandler, GeneralInstance4Channel, Channel, TimerPin}; +use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; pub use super::{Ch1, Ch2, Ch3, Ch4}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 2e05a0593..99afb5582 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -1,7 +1,7 @@ //! PWM Input driver. use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; -use super::{Ch1, Ch2, GeneralInstance4Channel, Channel, TimerPin}; +use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; use crate::gpio::{AfType, Pull}; use crate::time::Hertz; use crate::Peri; -- cgit From b4269f0f592955d12defade8a9f1769479bcf60f Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 01:47:05 +0200 Subject: [stm32h723]: fix spdifrx example --- examples/stm32h723/src/bin/spdifrx.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index a04d7cb34..61e77249d 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -7,9 +7,9 @@ use defmt::{info, trace}; use embassy_executor::Spawner; -use embassy_futures::select::{self, select, Either}; +use embassy_futures::select::{select, Either}; use embassy_stm32::spdifrx::{self, Spdifrx}; -use embassy_stm32::{bind_interrupts, peripherals, sai}; +use embassy_stm32::{bind_interrupts, peripherals, sai, Peri}; use grounded::uninit::GroundedArrayCell; use hal::sai::*; use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; @@ -144,9 +144,9 @@ async fn main(_spawner: Spawner) { /// /// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). fn new_spdif_receiver<'d>( - spdifrx: &'d mut peripherals::SPDIFRX1, - input_pin: &'d mut peripherals::PD7, - dma: &'d mut peripherals::DMA2_CH7, + spdifrx: Peri<'d, peripherals::SPDIFRX1>, + input_pin: Peri<'d, peripherals::PD7>, + dma: Peri<'d, peripherals::DMA2_CH7>, buf: &'d mut [u32], ) -> Spdifrx<'d, peripherals::SPDIFRX1> { Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) @@ -156,11 +156,11 @@ fn new_spdif_receiver<'d>( /// /// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). fn new_sai_transmitter<'d>( - sai: &'d mut peripherals::SAI4, - sck: &'d mut peripherals::PD13, - sd: &'d mut peripherals::PC1, - fs: &'d mut peripherals::PD12, - dma: &'d mut peripherals::BDMA_CH0, + sai: Peri<'d, peripherals::SAI4>, + sck: Peri<'d, peripherals::PD13>, + sd: Peri<'d, peripherals::PC1>, + fs: Peri<'d, peripherals::PD12>, + dma: Peri<'d, peripherals::BDMA_CH0>, buf: &'d mut [u32], ) -> Sai<'d, peripherals::SAI4, u32> { let mut sai_config = hal::sai::Config::default(); -- cgit From 688cac02716cf9122f82c315baa0a5e68265bb78 Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 02:07:58 +0200 Subject: add examples/stm32h723 to ci.sh --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index 33152e559..e54112359 100755 --- a/ci.sh +++ b/ci.sh @@ -246,6 +246,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \ --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \ --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \ + --- build --release --manifest-path examples/stm32h723/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h723 \ --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \ --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \ --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \ -- cgit From 686bdae937c26a006ee79b89931a6966141066dd Mon Sep 17 00:00:00 2001 From: melvdl Date: Fri, 27 Jun 2025 02:28:54 +0200 Subject: stm32h723: remove unused mut from static buffers in spdifrx example --- examples/stm32h723/src/bin/spdifrx.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index 61e77249d..6d29e8a4d 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -25,10 +25,10 @@ const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks // DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions #[unsafe(link_section = ".sram1")] -static mut SPDIFRX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); +static SPDIFRX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); #[unsafe(link_section = ".sram4")] -static mut SAI_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); +static SAI_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::main] async fn main(_spawner: Spawner) { -- cgit From 440b94aecf5964aeda192eb8bb5d1d2b8648e7e4 Mon Sep 17 00:00:00 2001 From: Iris Artin Date: Sun, 22 Jun 2025 00:05:49 -0400 Subject: Added STM32 ADCv1 analog watchdog implementation --- embassy-stm32/src/adc/v1.rs | 23 +++- embassy-stm32/src/adc/watchdog_v1.rs | 188 +++++++++++++++++++++++++++++++ examples/stm32f0/src/bin/adc-watchdog.rs | 34 ++++++ 3 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 embassy-stm32/src/adc/watchdog_v1.rs create mode 100644 examples/stm32f0/src/bin/adc-watchdog.rs diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index fb6f5b7d0..7fe502da0 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -11,6 +11,9 @@ use crate::interrupt::typelevel::Interrupt; use crate::peripherals::ADC1; use crate::{interrupt, rcc, Peri}; +mod watchdog_v1; +pub use watchdog_v1::WatchdogChannels; + pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -21,8 +24,15 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - if T::regs().isr().read().eoc() { + let isr = T::regs().isr().read(); + let ier = T::regs().ier().read(); + if ier.eocie() && isr.eoc() { + // eocie is set during adc.read() T::regs().ier().modify(|w| w.set_eocie(false)); + } else if ier.awdie() && isr.awd() { + // awdie is set during adc.monitor_watchdog() + T::regs().cr().read().set_adstp(true); + T::regs().ier().modify(|w| w.set_awdie(false)); } else { return; } @@ -186,16 +196,21 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().data() } -} -impl<'d, T: Instance> Drop for Adc<'d, T> { - fn drop(&mut self) { + fn teardown_adc() { // A.7.3 ADC disable code example T::regs().cr().modify(|reg| reg.set_adstp(true)); while T::regs().cr().read().adstp() {} T::regs().cr().modify(|reg| reg.set_addis(true)); while T::regs().cr().read().aden() {} + } +} + +impl<'d, T: Instance> Drop for Adc<'d, T> { + fn drop(&mut self) { + Self::teardown_adc(); + Self::teardown_awd(); rcc::disable::(); } diff --git a/embassy-stm32/src/adc/watchdog_v1.rs b/embassy-stm32/src/adc/watchdog_v1.rs new file mode 100644 index 000000000..bbe8e1971 --- /dev/null +++ b/embassy-stm32/src/adc/watchdog_v1.rs @@ -0,0 +1,188 @@ +use core::future::poll_fn; +use core::task::Poll; + +use stm32_metapac::adc::vals::{Align, Awdsgl, Res}; + +use crate::adc::{Adc, AdcChannel, Instance}; + +/// This enum is passed into `Adc::init_watchdog` to specify the channels for the watchdog to monitor +pub enum WatchdogChannels { + // Single channel identified by index + Single(u8), + // Multiple channels identified by mask + Multiple(u16), +} + +impl WatchdogChannels { + pub fn from_channel(channel: &impl AdcChannel) -> Self { + Self::Single(channel.channel()) + } + + pub fn add_channel(self, channel: &impl AdcChannel) -> Self { + WatchdogChannels::Multiple( + (match self { + WatchdogChannels::Single(ch) => 1 << ch, + WatchdogChannels::Multiple(ch) => ch, + }) | 1 << channel.channel(), + ) + } +} + +impl<'d, T: Instance> Adc<'d, T> { + /// Configure the analog window watchdog to monitor one or more ADC channels + /// + /// `high_threshold` and `low_threshold` are expressed in the same way as ADC results. The format + /// depends on the values of CFGR1.ALIGN and CFGR1.RES. + pub fn init_watchdog(&mut self, channels: WatchdogChannels, low_threshold: u16, high_threshold: u16) { + Self::stop_awd(); + + match channels { + WatchdogChannels::Single(ch) => { + T::regs().chselr().modify(|w| { + w.set_chsel_x(ch.into(), true); + }); + T::regs().cfgr1().modify(|w| { + w.set_awdch(ch); + w.set_awdsgl(Awdsgl::SINGLE_CHANNEL) + }); + } + WatchdogChannels::Multiple(ch) => { + T::regs().chselr().modify(|w| w.0 = ch.into()); + T::regs().cfgr1().modify(|w| { + w.set_awdch(0); + w.set_awdsgl(Awdsgl::ALL_CHANNELS) + }); + } + } + + Self::set_watchdog_thresholds(low_threshold, high_threshold); + Self::setup_awd(); + } + + /// Monitor the voltage on the selected channels; return when it crosses the thresholds. + /// + /// ```rust,ignore + /// // Wait for pin to go high + /// adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F); + /// let v_high = adc.monitor_watchdog().await; + /// info!("ADC sample is high {}", v_high); + /// ``` + pub async fn monitor_watchdog(&mut self) -> u16 { + assert!( + match T::regs().cfgr1().read().awdsgl() { + Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0, + Awdsgl::ALL_CHANNELS => T::regs().cfgr1().read().awdch() == 0, + }, + "`set_channel` should be called before `monitor`", + ); + assert!(T::regs().chselr().read().0 != 0); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); + Self::start_awd(); + + let sample = poll_fn(|cx| { + T::state().waker.register(cx.waker()); + + if T::regs().isr().read().awd() { + Poll::Ready(T::regs().dr().read().data()) + } else { + Poll::Pending + } + }) + .await; + + self.stop_watchdog(); + sample + } + + /// Stop monitoring the selected channels + pub fn stop_watchdog(&mut self) { + Self::stop_awd(); + } + + fn set_watchdog_thresholds(low_threshold: u16, high_threshold: u16) { + // This function takes `high_threshold` and `low_threshold` in the same alignment and resolution + // as ADC results, and programs them into ADC_DR. Because ADC_DR is always right-aligned on 12 bits, + // some bit-shifting may be necessary. See more in table 47 §13.7.1 Analog Watchdog Comparison + + // Verify that the thresholds are in the correct bit positions according to alignment and resolution + let threshold_mask = match (T::regs().cfgr1().read().align(), T::regs().cfgr1().read().res()) { + (Align::LEFT, Res::BITS6) => 0x00FC, + (Align::LEFT, Res::BITS8) => 0xFF00, + (Align::LEFT, Res::BITS10) => 0xFFC0, + (Align::LEFT, Res::BITS12) => 0xFFF0, + (Align::RIGHT, Res::BITS6) => 0x003F, + (Align::RIGHT, Res::BITS8) => 0x00FF, + (Align::RIGHT, Res::BITS10) => 0x03FF, + (Align::RIGHT, Res::BITS12) => 0x0FFF, + }; + assert!( + high_threshold & !threshold_mask == 0, + "High threshold {:x} is invalid — only bits {:x} are allowed", + high_threshold, + threshold_mask + ); + assert!( + low_threshold & !threshold_mask == 0, + "Low threshold {:x} is invalid — only bits {:x} are allowed", + low_threshold, + threshold_mask + ); + + T::regs().tr().modify(|w| { + w.set_lt(low_threshold << threshold_mask.leading_zeros() >> 4); + w.set_ht(high_threshold << threshold_mask.leading_zeros() >> 4); + }) + } + + fn setup_awd() { + // Configure AWD + assert!(!T::regs().cr().read().adstart()); + T::regs().cfgr1().modify(|w| w.set_awden(true)); + } + + fn start_awd() { + // Clear AWD interrupt flag + while T::regs().isr().read().awd() { + T::regs().isr().modify(|regs| { + regs.set_awd(true); + }) + } + + // Enable AWD interrupt + assert!(!T::regs().cr().read().adstart()); + T::regs().ier().modify(|w| { + w.set_eocie(false); + w.set_awdie(true) + }); + + // Start conversion + T::regs().cfgr1().modify(|w| w.set_cont(true)); + T::regs().cr().modify(|w| w.set_adstart(true)); + } + + fn stop_awd() { + // Stop conversion + while T::regs().cr().read().addis() {} + if T::regs().cr().read().adstart() { + T::regs().cr().write(|x| x.set_adstp(true)); + while T::regs().cr().read().adstp() {} + } + T::regs().cfgr1().modify(|w| w.set_cont(false)); + + // Disable AWD interrupt + assert!(!T::regs().cr().read().adstart()); + T::regs().ier().modify(|w| w.set_awdie(false)); + + // Clear AWD interrupt flag + while T::regs().isr().read().awd() { + T::regs().isr().modify(|regs| { + regs.set_awd(true); + }) + } + } + + pub(crate) fn teardown_awd() { + Self::stop_awd(); + T::regs().cfgr1().modify(|w| w.set_awden(false)); + } +} diff --git a/examples/stm32f0/src/bin/adc-watchdog.rs b/examples/stm32f0/src/bin/adc-watchdog.rs new file mode 100644 index 000000000..ff98aac8e --- /dev/null +++ b/examples/stm32f0/src/bin/adc-watchdog.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{self, Adc, WatchdogChannels}; +use embassy_stm32::bind_interrupts; +use embassy_stm32::peripherals::ADC1; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1_COMP => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("ADC watchdog example"); + + let mut adc = Adc::new(p.ADC1, Irqs); + let pin = p.PC1; + + loop { + // Wait for pin to go high + adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F); + let v_high = adc.monitor_watchdog().await; + info!("ADC sample is high {}", v_high); + + // Wait for pin to go low + adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0x01f, 0xFFF); + let v_low = adc.monitor_watchdog().await; + info!("ADC sample is low {}", v_low); + } +} -- cgit From 04bf17dde60c022ffaa5e37233ce45f048f0e2c3 Mon Sep 17 00:00:00 2001 From: Süha Ünüvar <87157627+phycrax@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:17:50 +0800 Subject: rename fns and add documentation --- embassy-stm32/src/timer/pwm_input.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 99afb5582..e5b7335e8 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -7,6 +7,10 @@ use crate::time::Hertz; use crate::Peri; /// PWM Input driver. +/// +/// Only works with CH1 and CH2 +/// Note: Not all timer peripherals are supported +/// Double check your chips reference manual pub struct PwmInput<'d, T: GeneralInstance4Channel> { channel: Channel, inner: Timer<'d, T>, @@ -14,14 +18,14 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Create a new PWM input driver. - pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { + pub fn new_ch1(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } /// Create a new PWM input driver. - pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { + pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) @@ -37,6 +41,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode + // or ST RM0440 (STM32G4) chapter 30.4.8 PWM input mode inner.set_input_ti_selection(ch1, InputTISelection::Normal); inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); -- cgit From 5cbc9a235f806bc961ed58da34ed932e39646e29 Mon Sep 17 00:00:00 2001 From: Süha Ünüvar <87157627+phycrax@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:27:56 +0800 Subject: correct documentation --- embassy-stm32/src/timer/pwm_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index e5b7335e8..1e55f2919 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -8,7 +8,7 @@ use crate::Peri; /// PWM Input driver. /// -/// Only works with CH1 and CH2 +/// Only works with CH1 or CH2 /// Note: Not all timer peripherals are supported /// Double check your chips reference manual pub struct PwmInput<'d, T: GeneralInstance4Channel> { -- cgit From b31a423eea6998ee681eda49433edc5dd03a5c63 Mon Sep 17 00:00:00 2001 From: Süha Ünüvar <87157627+phycrax@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:25:24 +0800 Subject: fix examples --- examples/stm32f1/src/bin/pwm_input.rs | 2 +- examples/stm32f4/src/bin/pwm_input.rs | 2 +- examples/stm32g0/src/bin/pwm_input.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index afbef3edb..aa6a11ff8 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PC13))); - let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(10)); + let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(10)); pwm_input.enable(); loop { diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index 465cbe4f5..74167cbf2 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB2))); - let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, khz(10)); + let mut pwm_input = PwmInput::new_ch1(p.TIM3, p.PA6, Pull::None, khz(10)); pwm_input.enable(); loop { diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index dc2428607..fd4f53f1e 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -47,7 +47,7 @@ async fn main(spawner: Spawner) { pwm.ch1().set_duty_cycle_fraction(1, 4); pwm.ch1().enable(); - let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); + let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(1000)); pwm_input.enable(); loop { -- cgit From 39c9cbcf49e248f799ac5e765750d90f9c698245 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Fri, 27 Jun 2025 20:15:14 +1000 Subject: Minimise profile tweaking in rp examples --- examples/rp/Cargo.toml | 10 ++-------- examples/rp235x/Cargo.toml | 7 ++----- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c8a132a5e..8d05d5a8c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -58,11 +58,5 @@ rand = { version = "0.9.0", default-features = false } embedded-sdmmc = "0.7.0" [profile.release] -debug = 2 -lto = true -opt-level = 'z' - -[profile.dev] -debug = 2 -lto = true -opt-level = "z" +# Enable generation of debug symbols even on release builds +debug = true diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 1346a75a2..c6afe37db 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -57,9 +57,6 @@ portable-atomic = { version = "1.5", features = ["critical-section"] } log = "0.4" embedded-sdmmc = "0.7.0" -[profile.dev] -debug = 2 - [profile.release] -lto = true -opt-level = "z" +# Enable generation of debug symbols even on release builds +debug = true -- cgit From 71975c72fceef48213b1ffd19bce8219c6d26de6 Mon Sep 17 00:00:00 2001 From: Kelsey Maes Date: Fri, 27 Jun 2025 14:11:01 -0700 Subject: mspm0: Fix inverted GPIO logic --- embassy-mspm0/src/gpio.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index 738d51928..9fbe34251 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs @@ -223,13 +223,13 @@ impl<'d> Flex<'d> { /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - !self.is_low() + self.pin.block().din31_0().read().dio(self.pin.bit_index()) } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - self.pin.block().din31_0().read().dio(self.pin.bit_index()) + !self.is_high() } /// Returns current pin level @@ -271,22 +271,22 @@ impl<'d> Flex<'d> { } } - /// Get the current pin input level. + /// Get the current pin output level. #[inline] pub fn get_output_level(&self) -> Level { - self.is_high().into() + self.is_set_high().into() } /// Is the output level high? #[inline] pub fn is_set_high(&self) -> bool { - !self.is_set_low() + self.pin.block().dout31_0().read().dio(self.pin.bit_index()) } /// Is the output level low? #[inline] pub fn is_set_low(&self) -> bool { - (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0 + !self.is_set_high() } /// Wait until the pin is high. If it is already high, return immediately. -- cgit From 15c7526c0a2e69880919392a5be7455156a2cba2 Mon Sep 17 00:00:00 2001 From: purepani Date: Mon, 30 Jun 2025 03:04:52 -0500 Subject: Updates stm32-metapac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 552113a79..d54f8dbae 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0069be7389d0378d826003304d72a13008f3ebce" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a3ce0ea3b5bd832ec2ad53465a0d80b0f4e0a" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0069be7389d0378d826003304d72a13008f3ebce", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a3ce0ea3b5bd832ec2ad53465a0d80b0f4e0a", default-features = false, features = ["metadata"] } [features] default = ["rt"] -- cgit From 08f3b45de67f195eb6b94b1dca478ee2d883cd0f Mon Sep 17 00:00:00 2001 From: purepani Date: Mon, 30 Jun 2025 03:04:52 -0500 Subject: Adds ADC4 for WBA --- embassy-stm32/build.rs | 4 + embassy-stm32/src/adc/adc4.rs | 542 +++++++++++++++++++++++++++++++++++++++ embassy-stm32/src/adc/mod.rs | 66 ++++- embassy-stm32/src/adc/u5_adc4.rs | 478 ---------------------------------- embassy-stm32/src/rcc/wba.rs | 1 + 5 files changed, 601 insertions(+), 490 deletions(-) create mode 100644 embassy-stm32/src/adc/adc4.rs delete mode 100644 embassy-stm32/src/adc/u5_adc4.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 13fc23e54..753f241c7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1490,6 +1490,10 @@ fn main() { signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } + if chip_name.starts_with("stm32wba") { + signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); + } + if chip_name.starts_with("stm32g4") { let line_number = chip_name.chars().skip(8).next().unwrap(); if line_number == '3' || line_number == '4' { diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs new file mode 100644 index 000000000..98483489f --- /dev/null +++ b/embassy-stm32/src/adc/adc4.rs @@ -0,0 +1,542 @@ +#[cfg(stm32u5)] +use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingRatio as OversamplingRatio}; +#[allow(unused)] +#[cfg(stm32wba)] +use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; + +use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; +use crate::dma::Transfer; +#[cfg(stm32u5)] +pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; +#[cfg(stm32wba)] +pub use crate::pac::adc::regs::Chselr; +#[cfg(stm32u5)] +pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime}; +#[cfg(stm32wba)] +pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; +use crate::time::Hertz; +use crate::{pac, rcc, Peri}; + +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); + +/// Default VREF voltage used for sample conversion to millivolts. +pub const VREF_DEFAULT_MV: u32 = 3300; +/// VREF voltage used for factory calibration of VREFINTCAL register. +pub const VREF_CALIB_MV: u32 = 3300; + +const VREF_CHANNEL: u8 = 0; +const VCORE_CHANNEL: u8 = 12; +const TEMP_CHANNEL: u8 = 13; +const VBAT_CHANNEL: u8 = 14; +const DAC_CHANNEL: u8 = 21; + +// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs +/// Internal voltage reference channel. +pub struct VrefInt; +impl AdcChannel for VrefInt {} +impl SealedAdcChannel for VrefInt { + fn channel(&self) -> u8 { + VREF_CHANNEL + } +} + +/// Internal temperature channel. +pub struct Temperature; +impl AdcChannel for Temperature {} +impl SealedAdcChannel for Temperature { + fn channel(&self) -> u8 { + TEMP_CHANNEL + } +} + +/// Internal battery voltage channel. +pub struct Vbat; +impl AdcChannel for Vbat {} +impl SealedAdcChannel for Vbat { + fn channel(&self) -> u8 { + VBAT_CHANNEL + } +} + +/// Internal DAC channel. +pub struct Dac; +impl AdcChannel for Dac {} +impl SealedAdcChannel for Dac { + fn channel(&self) -> u8 { + DAC_CHANNEL + } +} + +/// Internal Vcore channel. +pub struct Vcore; +impl AdcChannel for Vcore {} +impl SealedAdcChannel for Vcore { + fn channel(&self) -> u8 { + VCORE_CHANNEL + } +} + +pub enum DacChannel { + OUT1, + OUT2, +} + +/// Number of samples used for averaging. +pub enum Averaging { + Disabled, + Samples2, + Samples4, + Samples8, + Samples16, + Samples32, + Samples64, + Samples128, + Samples256, +} + +pub const fn resolution_to_max_count(res: Resolution) -> u32 { + match res { + Resolution::BITS12 => (1 << 12) - 1, + Resolution::BITS10 => (1 << 10) - 1, + Resolution::BITS8 => (1 << 8) - 1, + Resolution::BITS6 => (1 << 6) - 1, + #[allow(unreachable_patterns)] + _ => core::unreachable!(), + } +} + +// 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)] +enum Prescaler { + NotDivided, + DividedBy2, + DividedBy4, + DividedBy6, + DividedBy8, + DividedBy10, + DividedBy12, + DividedBy16, + DividedBy32, + DividedBy64, + DividedBy128, + DividedBy256, +} + +impl Prescaler { + fn from_ker_ck(frequency: Hertz) -> Self { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Self::NotDivided, + 1 => Self::DividedBy2, + 2..=3 => Self::DividedBy4, + 4..=5 => Self::DividedBy6, + 6..=7 => Self::DividedBy8, + 8..=9 => Self::DividedBy10, + 10..=11 => Self::DividedBy12, + _ => unimplemented!(), + } + } + + fn divisor(&self) -> u32 { + match self { + Prescaler::NotDivided => 1, + Prescaler::DividedBy2 => 2, + Prescaler::DividedBy4 => 4, + Prescaler::DividedBy6 => 6, + Prescaler::DividedBy8 => 8, + Prescaler::DividedBy10 => 10, + Prescaler::DividedBy12 => 12, + Prescaler::DividedBy16 => 16, + Prescaler::DividedBy32 => 32, + Prescaler::DividedBy64 => 64, + Prescaler::DividedBy128 => 128, + Prescaler::DividedBy256 => 256, + } + } + + fn presc(&self) -> Presc { + match self { + Prescaler::NotDivided => Presc::DIV1, + Prescaler::DividedBy2 => Presc::DIV2, + Prescaler::DividedBy4 => Presc::DIV4, + Prescaler::DividedBy6 => Presc::DIV6, + Prescaler::DividedBy8 => Presc::DIV8, + Prescaler::DividedBy10 => Presc::DIV10, + Prescaler::DividedBy12 => Presc::DIV12, + Prescaler::DividedBy16 => Presc::DIV16, + Prescaler::DividedBy32 => Presc::DIV32, + Prescaler::DividedBy64 => Presc::DIV64, + Prescaler::DividedBy128 => Presc::DIV128, + Prescaler::DividedBy256 => Presc::DIV256, + } + } +} + +pub trait SealedInstance { + #[allow(unused)] + fn regs() -> crate::pac::adc::Adc4; +} + +pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { + type Interrupt: crate::interrupt::typelevel::Interrupt; +} + +pub struct Adc4<'d, T: Instance> { + #[allow(unused)] + adc: crate::Peri<'d, T>, +} + +#[derive(Debug)] +pub enum Adc4Error { + InvalidSequence, + DMAError, +} + +impl<'d, T: Instance> Adc4<'d, T> { + /// Create a new ADC driver. + pub fn new(adc: Peri<'d, T>) -> Self { + rcc::enable_and_reset::(); + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + info!("ADC4 frequency set to {}", frequency); + + if frequency > MAX_ADC_CLK_FREQ { + panic!("Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + } + + let mut s = Self { adc }; + + s.power_up(); + + s.calibrate(); + blocking_delay_us(1); + + s.enable(); + s.configure(); + + s + } + + fn power_up(&mut self) { + T::regs().isr().modify(|w| { + w.set_ldordy(true); + }); + T::regs().cr().modify(|w| { + w.set_advregen(true); + }); + while !T::regs().isr().read().ldordy() {} + + T::regs().isr().modify(|w| { + w.set_ldordy(true); + }); + } + + fn calibrate(&mut self) { + T::regs().cr().modify(|w| w.set_adcal(true)); + while T::regs().cr().read().adcal() {} + T::regs().isr().modify(|w| w.set_eocal(true)); + } + + fn enable(&mut self) { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); + } + + fn configure(&mut self) { + // single conversion mode, software trigger + T::regs().cfgr1().modify(|w| { + #[cfg(stm32u5)] + w.set_cont(false); + #[cfg(stm32wba)] + w.set_cont(Cont::SINGLE); + w.set_discen(false); + w.set_exten(Exten::DISABLED); + #[cfg(stm32u5)] + w.set_chselrmod(false); + #[cfg(stm32wba)] + w.set_chselrmod(Chselrmod::ENABLE_INPUT); + }); + + // only use one channel at the moment + T::regs().smpr().modify(|w| { + #[cfg(stm32u5)] + for i in 0..24 { + w.set_smpsel(i, false); + } + #[cfg(stm32wba)] + for i in 0..14 { + w.set_smpsel(i, Smpsel::SMP1); + } + }); + } + + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> VrefInt { + T::regs().ccr().modify(|w| { + w.set_vrefen(true); + }); + + VrefInt {} + } + + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> Temperature { + T::regs().ccr().modify(|w| { + w.set_vsensesel(true); + }); + + Temperature {} + } + + /// Enable reading the vbat internal channel. + #[cfg(stm32u5)] + pub fn enable_vbat(&self) -> Vbat { + T::regs().ccr().modify(|w| { + w.set_vbaten(true); + }); + + Vbat {} + } + + /// Enable reading the vbat internal channel. + pub fn enable_vcore(&self) -> Vcore { + Vcore {} + } + + /// Enable reading the vbat internal channel. + #[cfg(stm32u5)] + pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { + let mux; + match dac { + DacChannel::OUT1 => mux = false, + DacChannel::OUT2 => mux = true, + } + T::regs().or().modify(|w| w.set_chn21sel(mux)); + Dac {} + } + + /// Set the ADC sample time. + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + T::regs().smpr().modify(|w| { + w.set_smp(0, sample_time); + }); + } + + /// Get the ADC sample time. + pub fn sample_time(&self) -> SampleTime { + T::regs().smpr().read().smp(0) + } + + /// Set the ADC resolution. + pub fn set_resolution(&mut self, resolution: Resolution) { + T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); + } + + /// Set hardware averaging. + #[cfg(stm32u5)] + pub fn set_averaging(&mut self, averaging: Averaging) { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), + Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), + Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, 2), + Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, 3), + Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, 4), + Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, 5), + Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, 6), + Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, 7), + Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, 8), + }; + + T::regs().cfgr2().modify(|w| { + w.set_ovsr(samples); + w.set_ovss(right_shift); + w.set_ovse(enable) + }) + } + #[cfg(stm32wba)] + pub fn set_averaging(&mut self, averaging: Averaging) { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), + Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), + Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, Ovss::SHIFT2), + Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, Ovss::SHIFT3), + Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, Ovss::SHIFT4), + Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, Ovss::SHIFT5), + Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, Ovss::SHIFT6), + Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, Ovss::SHIFT7), + Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, Ovss::SHIFT8), + }; + + T::regs().cfgr2().modify(|w| { + w.set_ovsr(samples); + w.set_ovss(right_shift); + w.set_ovse(enable) + }) + } + + /// Read an ADC channel. + pub fn blocking_read(&mut self, channel: &mut impl AdcChannel) -> u16 { + channel.setup(); + + // Select channel + #[cfg(stm32wba)] + { + T::regs().chselr().write_value(Chselr(0_u32)); + T::regs().chselr().modify(|w| { + w.set_chsel0(channel.channel() as usize, true); + }); + } + #[cfg(stm32u5)] + { + T::regs().chselrmod0().write_value(Chselr(0_u32)); + T::regs().chselrmod0().modify(|w| { + w.set_chsel(channel.channel() as usize, true); + }); + } + + // Reset interrupts + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !T::regs().isr().read().eos() { + // spin + } + + T::regs().dr().read().0 as u16 + } + + /// Read one or multiple ADC channels using DMA. + /// + /// `sequence` iterator and `readings` must have the same length. + /// The channels in `sequence` must be in ascending order. + /// + /// Example + /// ```rust,ignore + /// use embassy_stm32::adc::adc4; + /// use embassy_stm32::adc::AdcChannel; + /// + /// let mut adc4 = adc4::Adc4::new(p.ADC4); + /// let mut adc4_pin1 = p.PC1; + /// let mut adc4_pin2 = p.PC0; + /// let mut.into()d41 = adc4_pin1.into(); + /// let mut.into()d42 = adc4_pin2.into(); + /// let mut measurements = [0u16; 2]; + /// // not that the channels must be in ascending order + /// adc4.read( + /// &mut p.GPDMA1_CH1, + /// [ + /// &mut.into()d42, + /// &mut.into()d41, + /// ] + /// .into_iter(), + /// &mut measurements, + /// ).await.unwrap(); + /// ``` + pub async fn read( + &mut self, + rx_dma: Peri<'_, impl RxDma4>, + sequence: impl ExactSizeIterator>, + readings: &mut [u16], + ) -> Result<(), Adc4Error> { + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() == readings.len(), + "Sequence length must be equal to readings length" + ); + + // Ensure no conversions are ongoing + Self::cancel_conversions(); + + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + reg.set_eos(true); + reg.set_eoc(true); + }); + + T::regs().cfgr1().modify(|reg| { + reg.set_dmaen(true); + reg.set_dmacfg(Dmacfg::ONE_SHOT); + #[cfg(stm32u5)] + reg.set_chselrmod(false); + #[cfg(stm32wba)] + reg.set_chselrmod(Chselrmod::ENABLE_INPUT) + }); + + // Verify and activate sequence + let mut prev_channel: i16 = -1; + #[cfg(stm32wba)] + T::regs().chselr().write_value(Chselr(0_u32)); + #[cfg(stm32u5)] + T::regs().chselrmod0().write_value(Chselr(0_u32)); + for channel in sequence { + let channel_num = channel.channel; + if channel_num as i16 <= prev_channel { + return Err(Adc4Error::InvalidSequence); + }; + prev_channel = channel_num as i16; + + #[cfg(stm32wba)] + T::regs().chselr().modify(|w| { + w.set_chsel0(channel.channel as usize, true); + }); + #[cfg(stm32u5)] + T::regs().chselrmod0().modify(|w| { + w.set_chsel(channel.channel as usize, true); + }); + } + + let request = rx_dma.request(); + let transfer = unsafe { + Transfer::new_read( + rx_dma, + request, + T::regs().dr().as_ptr() as *mut u16, + readings, + Default::default(), + ) + }; + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + transfer.await; + + // Ensure conversions are finished. + Self::cancel_conversions(); + + // Reset configuration. + T::regs().cfgr1().modify(|reg| { + reg.set_dmaen(false); + }); + + if T::regs().isr().read().ovr() { + Err(Adc4Error::DMAError) + } else { + Ok(()) + } + } + + fn cancel_conversions() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(true); + }); + while T::regs().cr().read().adstart() {} + } + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index f46e87f38..778edc6f6 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -4,7 +4,7 @@ #![allow(missing_docs)] // TODO #![cfg_attr(adc_f3_v2, allow(unused))] -#[cfg(not(any(adc_f3_v2)))] +#[cfg(not(any(adc_f3_v2, adc_wba)))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_f3, path = "f3.rs")] #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] @@ -20,14 +20,14 @@ mod _version; use core::marker::PhantomData; #[allow(unused)] -#[cfg(not(any(adc_f3_v2)))] +#[cfg(not(any(adc_f3_v2, adc_wba)))] pub use _version::*; use embassy_hal_internal::{impl_peripheral, PeripheralType}; #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] use embassy_sync::waitqueue::AtomicWaker; -#[cfg(adc_u5)] -#[path = "u5_adc4.rs"] +#[cfg(any(adc_u5, adc_wba))] +#[path = "adc4.rs"] pub mod adc4; pub use crate::pac::adc::vals; @@ -36,15 +36,18 @@ pub use crate::pac::adc::vals::Res as Resolution; pub use crate::pac::adc::vals::SampleTime; use crate::peripherals; +#[cfg(not(adc_wba))] dma_trait!(RxDma, Instance); #[cfg(adc_u5)] dma_trait!(RxDma4, adc4::Instance); +#[cfg(adc_wba)] +dma_trait!(RxDma4, adc4::Instance); /// Analog to Digital driver. pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::Peri<'d, T>, - #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] + #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_wba)))] sample_time: SampleTime, } @@ -63,6 +66,7 @@ impl State { } trait SealedInstance { + #[cfg(not(adc_wba))] #[allow(unused)] fn regs() -> crate::pac::adc::Adc; #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] @@ -73,7 +77,7 @@ trait SealedInstance { } pub(crate) trait SealedAdcChannel { - #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] fn setup(&mut self) {} #[allow(unused)] @@ -110,7 +114,8 @@ pub(crate) fn blocking_delay_us(us: u32) { adc_h5, adc_h7rs, adc_u5, - adc_c0 + adc_c0, + adc_wba, )))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::PeripheralType { @@ -132,7 +137,8 @@ pub trait Instance: SealedInstance + crate::PeripheralType { adc_h5, adc_h7rs, adc_u5, - adc_c0 + adc_c0, + adc_wba, ))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { @@ -144,7 +150,7 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri pub trait AdcChannel: SealedAdcChannel + Sized { #[allow(unused_mut)] fn degrade_adc(mut self) -> AnyAdcChannel { - #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] + #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] self.setup(); AnyAdcChannel { @@ -176,6 +182,36 @@ impl AnyAdcChannel { self.channel } } +#[cfg(adc_wba)] +foreach_adc!( + (ADC4, $common_inst:ident, $clock:ident) => { + impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { + fn regs() -> crate::pac::adc::Adc4 { + crate::pac::ADC4 + } + } + + impl crate::adc::adc4::Instance for peripherals::ADC4 { + type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; + } + }; + + ($inst:ident, $common_inst:ident, $clock:ident) => { + impl crate::adc::SealedInstance for peripherals::$inst { + fn regs() -> crate::pac::adc::Adc { + crate::pac::$inst + } + + fn common_regs() -> crate::pac::adccommon::AdcCommon { + return crate::pac::$common_inst + } + } + + impl crate::adc::Instance for peripherals::$inst { + type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; + } + }; +); #[cfg(adc_u5)] foreach_adc!( @@ -208,15 +244,21 @@ foreach_adc!( }; ); -#[cfg(not(adc_u5))] +#[cfg(not(any(adc_u5, adc_wba)))] foreach_adc!( ($inst:ident, $common_inst:ident, $clock:ident) => { impl crate::adc::SealedInstance for peripherals::$inst { + #[cfg(not(adc_wba))] fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + #[cfg(adc_wba)] + fn regs() -> crate::pac::adc::Adc4 { + crate::pac::$inst + } + + #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5, adc_wba)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { return crate::pac::$common_inst } @@ -238,7 +280,7 @@ macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::adc::AdcChannel for crate::Peri<'_, crate::peripherals::$pin> {} impl crate::adc::SealedAdcChannel for crate::Peri<'_, crate::peripherals::$pin> { - #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] fn setup(&mut self) { ::set_as_analog(self); } diff --git a/embassy-stm32/src/adc/u5_adc4.rs b/embassy-stm32/src/adc/u5_adc4.rs deleted file mode 100644 index 1dd664366..000000000 --- a/embassy-stm32/src/adc/u5_adc4.rs +++ /dev/null @@ -1,478 +0,0 @@ -#[allow(unused)] -use pac::adc::vals::{Adc4Dmacfg, Adc4Exten, Adc4OversamplingRatio}; - -use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; -use crate::dma::Transfer; -pub use crate::pac::adc::regs::Adc4Chselrmod0; -pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime}; -use crate::time::Hertz; -use crate::{pac, rcc, Peri}; - -const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); - -/// Default VREF voltage used for sample conversion to millivolts. -pub const VREF_DEFAULT_MV: u32 = 3300; -/// VREF voltage used for factory calibration of VREFINTCAL register. -pub const VREF_CALIB_MV: u32 = 3300; - -const VREF_CHANNEL: u8 = 0; -const VCORE_CHANNEL: u8 = 12; -const TEMP_CHANNEL: u8 = 13; -const VBAT_CHANNEL: u8 = 14; -const DAC_CHANNEL: u8 = 21; - -// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs -/// Internal voltage reference channel. -pub struct VrefInt; -impl AdcChannel for VrefInt {} -impl SealedAdcChannel for VrefInt { - fn channel(&self) -> u8 { - VREF_CHANNEL - } -} - -/// Internal temperature channel. -pub struct Temperature; -impl AdcChannel for Temperature {} -impl SealedAdcChannel for Temperature { - fn channel(&self) -> u8 { - TEMP_CHANNEL - } -} - -/// Internal battery voltage channel. -pub struct Vbat; -impl AdcChannel for Vbat {} -impl SealedAdcChannel for Vbat { - fn channel(&self) -> u8 { - VBAT_CHANNEL - } -} - -/// Internal DAC channel. -pub struct Dac; -impl AdcChannel for Dac {} -impl SealedAdcChannel for Dac { - fn channel(&self) -> u8 { - DAC_CHANNEL - } -} - -/// Internal Vcore channel. -pub struct Vcore; -impl AdcChannel for Vcore {} -impl SealedAdcChannel for Vcore { - fn channel(&self) -> u8 { - VCORE_CHANNEL - } -} - -pub enum DacChannel { - OUT1, - OUT2, -} - -/// Number of samples used for averaging. -pub enum Averaging { - Disabled, - Samples2, - Samples4, - Samples8, - Samples16, - Samples32, - Samples64, - Samples128, - Samples256, -} - -pub const fn resolution_to_max_count(res: Resolution) -> u32 { - match res { - Resolution::BITS12 => (1 << 12) - 1, - Resolution::BITS10 => (1 << 10) - 1, - Resolution::BITS8 => (1 << 8) - 1, - Resolution::BITS6 => (1 << 6) - 1, - #[allow(unreachable_patterns)] - _ => core::unreachable!(), - } -} - -// 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)] -enum Prescaler { - NotDivided, - DividedBy2, - DividedBy4, - DividedBy6, - DividedBy8, - DividedBy10, - DividedBy12, - DividedBy16, - DividedBy32, - DividedBy64, - DividedBy128, - DividedBy256, -} - -impl Prescaler { - fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; - match raw_prescaler { - 0 => Self::NotDivided, - 1 => Self::DividedBy2, - 2..=3 => Self::DividedBy4, - 4..=5 => Self::DividedBy6, - 6..=7 => Self::DividedBy8, - 8..=9 => Self::DividedBy10, - 10..=11 => Self::DividedBy12, - _ => unimplemented!(), - } - } - - fn divisor(&self) -> u32 { - match self { - Prescaler::NotDivided => 1, - Prescaler::DividedBy2 => 2, - Prescaler::DividedBy4 => 4, - Prescaler::DividedBy6 => 6, - Prescaler::DividedBy8 => 8, - Prescaler::DividedBy10 => 10, - Prescaler::DividedBy12 => 12, - Prescaler::DividedBy16 => 16, - Prescaler::DividedBy32 => 32, - Prescaler::DividedBy64 => 64, - Prescaler::DividedBy128 => 128, - Prescaler::DividedBy256 => 256, - } - } - - fn presc(&self) -> Presc { - match self { - Prescaler::NotDivided => Presc::DIV1, - Prescaler::DividedBy2 => Presc::DIV2, - Prescaler::DividedBy4 => Presc::DIV4, - Prescaler::DividedBy6 => Presc::DIV6, - Prescaler::DividedBy8 => Presc::DIV8, - Prescaler::DividedBy10 => Presc::DIV10, - Prescaler::DividedBy12 => Presc::DIV12, - Prescaler::DividedBy16 => Presc::DIV16, - Prescaler::DividedBy32 => Presc::DIV32, - Prescaler::DividedBy64 => Presc::DIV64, - Prescaler::DividedBy128 => Presc::DIV128, - Prescaler::DividedBy256 => Presc::DIV256, - } - } -} - -pub trait SealedInstance { - #[allow(unused)] - fn regs() -> crate::pac::adc::Adc4; -} - -pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { - type Interrupt: crate::interrupt::typelevel::Interrupt; -} - -pub struct Adc4<'d, T: Instance> { - #[allow(unused)] - adc: crate::Peri<'d, T>, -} - -#[derive(Debug)] -pub enum Adc4Error { - InvalidSequence, - DMAError, -} - -impl<'d, T: Instance> Adc4<'d, T> { - /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { - rcc::enable_and_reset::(); - let prescaler = Prescaler::from_ker_ck(T::frequency()); - - T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); - - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); - info!("ADC4 frequency set to {}", frequency); - - if frequency > MAX_ADC_CLK_FREQ { - panic!("Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); - } - - let mut s = Self { adc }; - - s.power_up(); - - s.calibrate(); - blocking_delay_us(1); - - s.enable(); - s.configure(); - - s - } - - fn power_up(&mut self) { - T::regs().isr().modify(|w| { - w.set_ldordy(true); - }); - T::regs().cr().modify(|w| { - w.set_advregen(true); - }); - while !T::regs().isr().read().ldordy() {} - - T::regs().isr().modify(|w| { - w.set_ldordy(true); - }); - } - - fn calibrate(&mut self) { - T::regs().cr().modify(|w| w.set_adcal(true)); - while T::regs().cr().read().adcal() {} - T::regs().isr().modify(|w| w.set_eocal(true)); - } - - fn enable(&mut self) { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } - - fn configure(&mut self) { - // single conversion mode, software trigger - T::regs().cfgr1().modify(|w| { - w.set_cont(false); - w.set_discen(false); - w.set_exten(Adc4Exten::DISABLED); - w.set_chselrmod(false); - }); - - // only use one channel at the moment - T::regs().smpr().modify(|w| { - for i in 0..24 { - w.set_smpsel(i, false); - } - }); - } - - /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> VrefInt { - T::regs().ccr().modify(|w| { - w.set_vrefen(true); - }); - - VrefInt {} - } - - /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> Temperature { - T::regs().ccr().modify(|w| { - w.set_vsensesel(true); - }); - - Temperature {} - } - - /// Enable reading the vbat internal channel. - pub fn enable_vbat(&self) -> Vbat { - T::regs().ccr().modify(|w| { - w.set_vbaten(true); - }); - - Vbat {} - } - - /// Enable reading the vbat internal channel. - pub fn enable_vcore(&self) -> Vcore { - Vcore {} - } - - /// Enable reading the vbat internal channel. - pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { - let mux; - match dac { - DacChannel::OUT1 => mux = false, - DacChannel::OUT2 => mux = true, - } - T::regs().or().modify(|w| w.set_chn21sel(mux)); - Dac {} - } - - /// Set the ADC sample time. - pub fn set_sample_time(&mut self, sample_time: SampleTime) { - T::regs().smpr().modify(|w| { - w.set_smp(0, sample_time); - }); - } - - /// Get the ADC sample time. - pub fn sample_time(&self) -> SampleTime { - T::regs().smpr().read().smp(0) - } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); - } - - /// Set hardware averaging. - pub fn set_averaging(&mut self, averaging: Averaging) { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, Adc4OversamplingRatio::OVERSAMPLE2X, 0), - Averaging::Samples2 => (true, Adc4OversamplingRatio::OVERSAMPLE2X, 1), - Averaging::Samples4 => (true, Adc4OversamplingRatio::OVERSAMPLE4X, 2), - Averaging::Samples8 => (true, Adc4OversamplingRatio::OVERSAMPLE8X, 3), - Averaging::Samples16 => (true, Adc4OversamplingRatio::OVERSAMPLE16X, 4), - Averaging::Samples32 => (true, Adc4OversamplingRatio::OVERSAMPLE32X, 5), - Averaging::Samples64 => (true, Adc4OversamplingRatio::OVERSAMPLE64X, 6), - Averaging::Samples128 => (true, Adc4OversamplingRatio::OVERSAMPLE128X, 7), - Averaging::Samples256 => (true, Adc4OversamplingRatio::OVERSAMPLE256X, 8), - }; - - T::regs().cfgr2().modify(|w| { - w.set_ovsr(samples); - w.set_ovss(right_shift); - w.set_ovse(enable) - }) - } - - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel) -> u16 { - channel.setup(); - - // Select channel - T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); - T::regs().chselrmod0().modify(|w| { - w.set_chsel(channel.channel() as usize, true); - }); - - // Reset interrupts - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - while !T::regs().isr().read().eos() { - // spin - } - - T::regs().dr().read().0 as u16 - } - - /// Read one or multiple ADC channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// The channels in `sequence` must be in ascending order. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::adc4; - /// use embassy_stm32::adc::AdcChannel; - /// - /// let mut adc4 = adc4::Adc4::new(p.ADC4); - /// let mut adc4_pin1 = p.PC1; - /// let mut adc4_pin2 = p.PC0; - /// let mut.into()d41 = adc4_pin1.into(); - /// let mut.into()d42 = adc4_pin2.into(); - /// let mut measurements = [0u16; 2]; - /// // not that the channels must be in ascending order - /// adc4.read( - /// &mut p.GPDMA1_CH1, - /// [ - /// &mut.into()d42, - /// &mut.into()d41, - /// ] - /// .into_iter(), - /// &mut measurements, - /// ).await.unwrap(); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma4>, - sequence: impl ExactSizeIterator>, - readings: &mut [u16], - ) -> Result<(), Adc4Error> { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - - // Ensure no conversions are ongoing - Self::cancel_conversions(); - - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - reg.set_eos(true); - reg.set_eoc(true); - }); - - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(true); - reg.set_dmacfg(Adc4Dmacfg::ONE_SHOT); - reg.set_chselrmod(false); - }); - - // Verify and activate sequence - let mut prev_channel: i16 = -1; - T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); - for channel in sequence { - let channel_num = channel.channel; - if channel_num as i16 <= prev_channel { - return Err(Adc4Error::InvalidSequence); - }; - prev_channel = channel_num as i16; - - T::regs().chselrmod0().modify(|w| { - w.set_chsel(channel.channel as usize, true); - }); - } - - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(false); - }); - - if T::regs().isr().read().ovr() { - Err(Adc4Error::DMAError) - } else { - Ok(()) - } - } - - fn cancel_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); - while T::regs().cr().read().adstart() {} - } - } -} diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index b9fc4e423..b494997b3 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs @@ -176,6 +176,7 @@ pub(crate) unsafe fn init(config: Config) { // TODO lse: None, lsi: None, + pll1_p: None, pll1_q: None, ); } -- cgit From f597901879f353fcdd59b4a77505309c2aaed187 Mon Sep 17 00:00:00 2001 From: purepani Date: Mon, 30 Jun 2025 03:04:52 -0500 Subject: Adds ADC example for STM32WBA --- examples/stm32wba/src/bin/adc.rs | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/stm32wba/src/bin/adc.rs diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs new file mode 100644 index 000000000..a9651d57e --- /dev/null +++ b/examples/stm32wba/src/bin/adc.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::adc::{adc4, AdcChannel}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let config = embassy_stm32::Config::default(); + + let mut p = embassy_stm32::init(config); + + // **** ADC4 init **** + let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4_pin1 = p.PA0; // A4 + let mut adc4_pin2 = p.PA1; // A5 + adc4.set_resolution(adc4::Resolution::BITS12); + adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); + let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); + + // **** ADC4 blocking read **** + let raw: u16 = adc4.blocking_read(&mut adc4_pin1); + let volt: f32 = 3.0 * raw as f32 / max4 as f32; + info!("Read adc4 pin 1 {}", volt); + + let raw: u16 = adc4.blocking_read(&mut adc4_pin2); + let volt: f32 = 3.3 * raw as f32 / max4 as f32; + info!("Read adc4 pin 2 {}", volt); + + // **** ADC4 async read **** + let mut degraded41 = adc4_pin1.degrade_adc(); + let mut degraded42 = adc4_pin2.degrade_adc(); + let mut measurements = [0u16; 2]; + + // The channels must be in ascending order and can't repeat for ADC4 + adc4.read( + p.GPDMA1_CH1.reborrow(), + [&mut degraded42, &mut degraded41].into_iter(), + &mut measurements, + ) + .await + .unwrap(); + let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; + let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; + info!("Async read 4 pin 1 {}", volt1); + info!("Async read 4 pin 2 {}", volt2); +} -- cgit From 8d2657383e29c78bd6b4a4dd7c2977ee970c636d Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Wed, 2 Jul 2025 10:33:16 +0200 Subject: Link to esp-generate --- docs/pages/new_project.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index cd943b4f6..906d89f36 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -11,6 +11,8 @@ Once you’ve successfully xref:#_getting_started[run some example projects], th - link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) - link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) +=== esp-generate +- link:https://github.com/esp-rs/esp-generate[esp-generate] (ESP32 using esp-hal) == Starting a project from scratch -- cgit From 30e6d633d3b54b9d34894e9c4b3ce32571115983 Mon Sep 17 00:00:00 2001 From: Juergen Fitschen Date: Wed, 2 Jul 2025 11:39:07 +0200 Subject: embassy-nrf: fix PWM loop count --- embassy-nrf/src/pwm.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index a2e153e26..3d76272ac 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -479,9 +479,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 }; match times { - // just the one time, no loop count - SequenceMode::Loop(_) => { - r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED)); + SequenceMode::Loop(n) => { + r.loop_().write(|w| w.set_cnt(vals::LoopCnt(n))); } // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again SequenceMode::Infinite => { -- cgit From 3c0d5063fe59c6831fbcfeae04338606aec05d05 Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Wed, 2 Jul 2025 12:31:38 -0500 Subject: rp: add current_core api --- embassy-rp/src/multicore.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index d10b6837c..64065fcba 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -57,6 +57,26 @@ const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); +/// Represents a partiticular CPU core (SIO_CPUID) +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum CoreId { + /// Core 0 + Core0 = 0x0, + /// Core 1 + Core1 = 0x1, +} + +/// Gets which core we are currently executing from +pub fn current_core() -> CoreId { + if pac::SIO.cpuid().read() == 0 { + CoreId::Core0 + } else { + CoreId::Core1 + } +} + #[inline(always)] unsafe fn core1_setup(stack_bottom: *mut usize) { if install_stack_guard(stack_bottom).is_err() { -- cgit From db9af5b6cc2d5f47a912836bebf25b48903006f9 Mon Sep 17 00:00:00 2001 From: Sam W Date: Thu, 3 Jul 2025 14:52:33 +0100 Subject: net: correct `UdpSocket::recv_from_with` docs --- embassy-net/src/udp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 63c2f4c75..482eb0e56 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -172,7 +172,7 @@ impl<'a> UdpSocket<'a> { /// register the current task to be notified when a datagram is received. /// /// When a datagram is received, this method will call the provided function - /// with the number of bytes received and the remote endpoint and return + /// with a reference to the received bytes and the remote endpoint and return /// `Poll::Ready` with the function's returned value. pub async fn recv_from_with(&mut self, f: F) -> R where -- cgit From e256f1360b4dff74f712b35db6a5f1bdf794a131 Mon Sep 17 00:00:00 2001 From: qwerty19106 Date: Thu, 3 Jul 2025 18:02:50 +0400 Subject: Fix impl embedded_hal_nb::serial::Write for embassy_stm32::usart::UartTx --- embassy-stm32/src/usart/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b3f8bc00c..8c9028f08 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -550,6 +550,20 @@ impl<'d, M: Mode> UartTx<'d, M> { reconfigure(self.info, self.kernel_clock, config) } + /// Write a single u8 if there is tx empty, otherwise return WouldBlock + pub(crate) fn nb_write(&mut self, byte: u8) -> Result<(), nb::Error> { + let r = self.info.regs; + let sr = sr(r).read(); + if sr.txe() { + unsafe { + tdr(r).write_volatile(byte); + } + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + /// Perform a blocking UART write pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { let r = self.info.regs; @@ -1864,7 +1878,7 @@ impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> { impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { - self.blocking_write(&[char]).map_err(nb::Error::Other) + self.nb_write(char) } fn flush(&mut self) -> nb::Result<(), Self::Error> { -- cgit From 388eee221e16717fe47913d26a1f6ed4cd35d4bc Mon Sep 17 00:00:00 2001 From: Matt Bhagat-Conway Date: Thu, 3 Jul 2025 10:31:28 -0400 Subject: add note about UART line breaks being different from ASCII --- embassy-rp/src/uart/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index c3a15fda5..b730d33e3 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -515,6 +515,9 @@ impl<'d> UartRx<'d, Async> { /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: /// * The first call to `read_to_break()` will return `Ok(20)`. /// * The next call to `read_to_break()` will work as expected + /// + /// **NOTE**: In the UART context, a line break refers to a break condition (the line being held low for + /// for longer than a single character), not an ASCII line break. pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result { self.read_to_break_with_count(buffer, 0).await } @@ -541,6 +544,9 @@ impl<'d> UartRx<'d, Async> { /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: /// * The first call to `read_to_break()` will return `Ok(20)`. /// * The next call to `read_to_break()` will work as expected + /// + /// **NOTE**: In the UART context, a line break refers to a break condition (the line being held low for + /// for longer than a single character), not an ASCII line break. pub async fn read_to_break_with_count( &mut self, buffer: &mut [u8], -- cgit From 6545b051887cd6944557901015b4c5b54a2b6848 Mon Sep 17 00:00:00 2001 From: Matt Bhagat-Conway Date: Thu, 3 Jul 2025 10:37:49 -0400 Subject: fix rustfmt in read_to_break docstring --- embassy-rp/src/uart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index b730d33e3..cfbce493c 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -544,7 +544,7 @@ impl<'d> UartRx<'d, Async> { /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: /// * The first call to `read_to_break()` will return `Ok(20)`. /// * The next call to `read_to_break()` will work as expected - /// + /// /// **NOTE**: In the UART context, a line break refers to a break condition (the line being held low for /// for longer than a single character), not an ASCII line break. pub async fn read_to_break_with_count( -- cgit From da6c4ff31a3ebc9995c695c0ad9bc260c5ccffed Mon Sep 17 00:00:00 2001 From: Matt Bhagat-Conway Date: Thu, 3 Jul 2025 11:54:00 -0400 Subject: remove line break reference from documentation --- embassy-rp/src/uart/mod.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index cfbce493c..6f4e2ee07 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -495,57 +495,57 @@ impl<'d> UartRx<'d, Async> { unreachable!("unrecognized rx error"); } - /// Read from the UART, waiting for a line break. + /// Read from the UART, waiting for a break. /// /// We read until one of the following occurs: /// - /// * We read `buffer.len()` bytes without a line break + /// * We read `buffer.len()` bytes without a break /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` - /// * We read `n` bytes then a line break occurs + /// * We read `n` bytes then a break occurs /// * returns `Ok(n)` - /// * We encounter some error OTHER than a line break + /// * We encounter some error OTHER than a break /// * returns `Err(ReadToBreakError::Other(error))` /// /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected /// message to reliably detect the framing on one single call to `read_to_break()`. /// - /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: + /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer: /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` - /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break - /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: + /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" break + /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer: /// * The first call to `read_to_break()` will return `Ok(20)`. /// * The next call to `read_to_break()` will work as expected /// - /// **NOTE**: In the UART context, a line break refers to a break condition (the line being held low for + /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for /// for longer than a single character), not an ASCII line break. pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result { self.read_to_break_with_count(buffer, 0).await } - /// Read from the UART, waiting for a line break as soon as at least `min_count` bytes have been read. + /// Read from the UART, waiting for a break as soon as at least `min_count` bytes have been read. /// /// We read until one of the following occurs: /// - /// * We read `buffer.len()` bytes without a line break + /// * We read `buffer.len()` bytes without a break /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` - /// * We read `n > min_count` bytes then a line break occurs + /// * We read `n > min_count` bytes then a break occurs /// * returns `Ok(n)` - /// * We encounter some error OTHER than a line break + /// * We encounter some error OTHER than a break /// * returns `Err(ReadToBreakError::Other(error))` /// - /// If a line break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue + /// If a break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue /// /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected /// message to reliably detect the framing on one single call to `read_to_break()`. /// - /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: + /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer: /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break - /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: + /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer: /// * The first call to `read_to_break()` will return `Ok(20)`. /// * The next call to `read_to_break()` will work as expected /// - /// **NOTE**: In the UART context, a line break refers to a break condition (the line being held low for + /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for /// for longer than a single character), not an ASCII line break. pub async fn read_to_break_with_count( &mut self, -- cgit From 72248a601a9ea28ac696f186e2cbe4c2f128a133 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 29 Jun 2025 22:37:11 +0200 Subject: Update Rust nightly, stable. --- embassy-executor/build_common.rs | 32 ----------- .../tests/ui/bad_return_impl_future.stderr | 32 +++++------ embassy-executor/tests/ui/return_impl_send.stderr | 64 +++++++++++----------- embassy-nrf/src/uarte.rs | 2 +- embassy-rp/src/flash.rs | 6 +- embassy-rp/src/relocate.rs | 2 +- .../src/can/fd/message_ram/extended_filter.rs | 8 +-- .../src/can/fd/message_ram/standard_filter.rs | 8 +-- .../src/can/fd/message_ram/txbuffer_element.rs | 18 +++--- embassy-stm32/src/cordic/utils.rs | 2 +- embassy-stm32/src/tsc/acquisition_banks.rs | 6 +- embassy-sync/src/pubsub/mod.rs | 8 +-- embassy-usb/src/class/web_usb.rs | 2 +- rust-toolchain-nightly.toml | 2 +- rust-toolchain.toml | 2 +- 15 files changed, 83 insertions(+), 111 deletions(-) diff --git a/embassy-executor/build_common.rs b/embassy-executor/build_common.rs index b15a8369f..4f24e6d37 100644 --- a/embassy-executor/build_common.rs +++ b/embassy-executor/build_common.rs @@ -92,35 +92,3 @@ pub fn set_target_cfgs(cfgs: &mut CfgSet) { cfgs.set("has_fpu", target.ends_with("-eabihf")); } - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct CompilerDate { - year: u16, - month: u8, - day: u8, -} - -impl CompilerDate { - fn parse(date: &str) -> Option { - let mut parts = date.split('-'); - let year = parts.next()?.parse().ok()?; - let month = parts.next()?.parse().ok()?; - let day = parts.next()?.parse().ok()?; - Some(Self { year, month, day }) - } -} - -impl PartialEq<&str> for CompilerDate { - fn eq(&self, other: &&str) -> bool { - let Some(other) = Self::parse(other) else { - return false; - }; - self.eq(&other) - } -} - -impl PartialOrd<&str> for CompilerDate { - fn partial_cmp(&self, other: &&str) -> Option { - Self::parse(other).map(|other| self.cmp(&other)) - } -} diff --git a/embassy-executor/tests/ui/bad_return_impl_future.stderr b/embassy-executor/tests/ui/bad_return_impl_future.stderr index 2980fd18b..57f147714 100644 --- a/embassy-executor/tests/ui/bad_return_impl_future.stderr +++ b/embassy-executor/tests/ui/bad_return_impl_future.stderr @@ -68,6 +68,22 @@ note: required by a bound in `task_pool_align` | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0277]: task futures must resolve to `()` or `!` + --> tests/ui/bad_return_impl_future.rs:5:4 + | +4 | #[embassy_executor::task] + | ------------------------- required by a bound introduced by this call +5 | fn task() -> impl Future { + | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` + | + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `__task_pool_get` + --> tests/ui/bad_return_impl_future.rs:4:1 + | +4 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0277]: task futures must resolve to `()` or `!` --> tests/ui/bad_return_impl_future.rs:5:4 | @@ -102,19 +118,3 @@ note: required by a bound in `task_pool_new` | F: TaskFn, | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: task futures must resolve to `()` or `!` - --> tests/ui/bad_return_impl_future.rs:5:4 - | -4 | #[embassy_executor::task] - | ------------------------- required by a bound introduced by this call -5 | fn task() -> impl Future { - | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future {__task_task}` - | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `__task_pool_get` - --> tests/ui/bad_return_impl_future.rs:4:1 - | -4 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` - = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-executor/tests/ui/return_impl_send.stderr b/embassy-executor/tests/ui/return_impl_send.stderr index 7e3e468b8..cd693af2b 100644 --- a/embassy-executor/tests/ui/return_impl_send.stderr +++ b/embassy-executor/tests/ui/return_impl_send.stderr @@ -77,30 +77,28 @@ error[E0277]: task futures must resolve to `()` or `!` | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_new` - --> src/lib.rs +note: required by a bound in `__task_pool_get` + --> tests/ui/return_impl_send.rs:3:1 | - | pub const fn task_pool_new(_: F) -> TaskPool - | ------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^ required by this bound in `task_pool_new` +3 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: task futures must resolve to `()` or `!` +error[E0277]: `impl Send` is not a future --> tests/ui/return_impl_send.rs:3:1 | 3 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future | - = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `task_pool_new` - --> src/lib.rs + = help: the trait `Future` is not implemented for `impl Send` +note: required by a bound in `TaskPool::::_spawn_async_fn` + --> src/raw/mod.rs | - | pub const fn task_pool_new(_: F) -> TaskPool - | ------------- required by a bound in this function - | where - | F: TaskFn, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` + | impl TaskPool { + | ^^^^^^ required by this bound in `TaskPool::::_spawn_async_fn` +... + | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + | --------------- required by a bound in this associated function = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: task futures must resolve to `()` or `!` @@ -112,26 +110,28 @@ error[E0277]: task futures must resolve to `()` or `!` | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | = note: use `async fn` or change the return type to `impl Future` -note: required by a bound in `__task_pool_get` - --> tests/ui/return_impl_send.rs:3:1 +note: required by a bound in `task_pool_new` + --> src/lib.rs | -3 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` - = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^ required by this bound in `task_pool_new` -error[E0277]: `impl Send` is not a future +error[E0277]: task futures must resolve to `()` or `!` --> tests/ui/return_impl_send.rs:3:1 | 3 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | - = help: the trait `Future` is not implemented for `impl Send` -note: required by a bound in `TaskPool::::_spawn_async_fn` - --> src/raw/mod.rs + = note: use `async fn` or change the return type to `impl Future` +note: required by a bound in `task_pool_new` + --> src/lib.rs | - | impl TaskPool { - | ^^^^^^ required by this bound in `TaskPool::::_spawn_async_fn` -... - | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - | --------------- required by a bound in this associated function + | pub const fn task_pool_new(_: F) -> TaskPool + | ------------- required by a bound in this function + | where + | F: TaskFn, + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index f377df49e..927a0ac08 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -245,7 +245,7 @@ impl<'d, T: Instance> Uarte<'d, T> { } /// Return the endtx event for use with PPI - pub fn event_endtx(&self) -> Event { + pub fn event_endtx(&self) -> Event<'_> { let r = T::regs(); Event::from_reg(r.events_endtx()) } diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index ef1cd9212..8c809090e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -482,7 +482,11 @@ mod ram_helpers { /// # 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 { + 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 { diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index 34487819f..6ff40ddd7 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -39,7 +39,7 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { } impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { - pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram { + pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram<'_, PROGRAM_SIZE> { RelocatedProgram { program, origin } } diff --git a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs index 453e9056e..ac47901a8 100644 --- a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs +++ b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs @@ -115,22 +115,22 @@ impl R { impl W { #[doc = "Byte 0 - Bits 0:28 - EFID1"] #[inline(always)] - pub fn efid1(&mut self) -> EFID1_W { + pub fn efid1(&mut self) -> EFID1_W<'_> { EFID1_W { w: self } } #[doc = "Byte 0 - Bits 29:31 - EFEC"] #[inline(always)] - pub fn efec(&mut self) -> EFEC_W { + pub fn efec(&mut self) -> EFEC_W<'_> { EFEC_W { w: self } } #[doc = "Byte 1 - Bits 0:28 - EFID2"] #[inline(always)] - pub fn efid2(&mut self) -> EFID2_W { + pub fn efid2(&mut self) -> EFID2_W<'_> { EFID2_W { w: self } } #[doc = "Byte 1 - Bits 30:31 - EFT"] #[inline(always)] - pub fn eft(&mut self) -> EFT_W { + pub fn eft(&mut self) -> EFT_W<'_> { EFT_W { w: self } } } diff --git a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs index 3a3bbcf12..f52646bfe 100644 --- a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs +++ b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs @@ -115,22 +115,22 @@ impl R { impl W { #[doc = "Bits 0:10 - SFID2"] #[inline(always)] - pub fn sfid2(&mut self) -> SFID2_W { + pub fn sfid2(&mut self) -> SFID2_W<'_> { SFID2_W { w: self } } #[doc = "Bits 16:26 - SFID1"] #[inline(always)] - pub fn sfid1(&mut self) -> SFID1_W { + pub fn sfid1(&mut self) -> SFID1_W<'_> { SFID1_W { w: self } } #[doc = "Bits 27:29 - SFEC"] #[inline(always)] - pub fn sfec(&mut self) -> SFEC_W { + pub fn sfec(&mut self) -> SFEC_W<'_> { SFEC_W { w: self } } #[doc = "Bits 30:31 - SFT"] #[inline(always)] - pub fn sft(&mut self) -> SFT_W { + pub fn sft(&mut self) -> SFT_W<'_> { SFT_W { w: self } } } diff --git a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs index 455406a1c..6d65a86cb 100644 --- a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs +++ b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs @@ -376,47 +376,47 @@ impl R { impl W { #[doc = "Byte 0 - Bits 0:28 - ID"] #[inline(always)] - pub fn id(&mut self) -> ID_W { + pub fn id(&mut self) -> ID_W<'_> { ID_W { w: self } } #[doc = "Byte 0 - Bit 29 - RTR"] #[inline(always)] - pub fn rtr(&mut self) -> RTR_W { + pub fn rtr(&mut self) -> RTR_W<'_> { RTR_W { w: self } } #[doc = "Byte 0 - Bit 30 - XTD"] #[inline(always)] - pub fn xtd(&mut self) -> XTD_W { + pub fn xtd(&mut self) -> XTD_W<'_> { XTD_W { w: self } } #[doc = "Byte 0 - Bit 31 - ESI"] #[inline(always)] - pub fn esi(&mut self) -> ESI_W { + pub fn esi(&mut self) -> ESI_W<'_> { ESI_W { w: self } } #[doc = "Byte 1 - Bit 16:19 - DLC"] #[inline(always)] - pub fn dlc(&mut self) -> DLC_W { + pub fn dlc(&mut self) -> DLC_W<'_> { DLC_W { w: self } } #[doc = "Byte 1 - Bit 20 - BRS"] #[inline(always)] - pub fn brs(&mut self) -> BRS_W { + pub fn brs(&mut self) -> BRS_W<'_> { BRS_W { w: self } } #[doc = "Byte 1 - Bit 21 - FDF"] #[inline(always)] - pub fn fdf(&mut self) -> FDF_W { + pub fn fdf(&mut self) -> FDF_W<'_> { FDF_W { w: self } } #[doc = "Byte 1 - Bit 23 - EFC"] #[inline(always)] - pub fn efc(&mut self) -> EFC_W { + pub fn efc(&mut self) -> EFC_W<'_> { EFC_W { w: self } } #[doc = "Byte 1 - Bit 24:31 - MM"] #[inline(always)] - pub fn mm(&mut self) -> MM_W { + pub fn mm(&mut self) -> MM_W<'_> { MM_W { w: self } } #[doc = "Convenience function for setting the data length and frame format"] diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 008f50270..9afa8ef53 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs @@ -5,7 +5,7 @@ macro_rules! floating_fixed_convert { ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { /// convert float point to fixed point format pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> { - const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; + const MIN_POSITIVE: $float_ty = <$float_ty>::from_bits($min_positive); if value < -1.0 { return Err(NumberOutOfRange::BelowLowerBound) diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs index 6791ef6c1..7d6442b48 100644 --- a/embassy-stm32/src/tsc/acquisition_banks.rs +++ b/embassy-stm32/src/tsc/acquisition_banks.rs @@ -32,7 +32,7 @@ impl AcquisitionBankPins { /// Returns an iterator over the pins in this acquisition bank. /// /// This method allows for easy traversal of all configured pins in the bank. - pub fn iter(&self) -> AcquisitionBankPinsIterator { + pub fn iter(&self) -> AcquisitionBankPinsIterator<'_> { AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) } } @@ -90,7 +90,7 @@ impl<'a> Iterator for AcquisitionBankPinsIterator<'a> { impl AcquisitionBankPins { /// Returns an iterator over the available pins in the bank - pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { + pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> { AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) } } @@ -107,7 +107,7 @@ pub struct AcquisitionBank { impl AcquisitionBank { /// Returns an iterator over the available pins in the bank. - pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { + pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> { self.pins.pins_iterator() } diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 606efff0a..9206b9383 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -88,7 +88,7 @@ impl Result, Error> { + pub fn subscriber(&self) -> Result, Error> { self.inner.lock(|inner| { let mut s = inner.borrow_mut(); @@ -120,7 +120,7 @@ impl Result, Error> { + pub fn publisher(&self) -> Result, Error> { self.inner.lock(|inner| { let mut s = inner.borrow_mut(); @@ -151,13 +151,13 @@ impl ImmediatePublisher { + pub fn immediate_publisher(&self) -> ImmediatePublisher<'_, M, T, CAP, SUBS, PUBS> { ImmediatePublisher(ImmediatePub::new(self)) } /// Create a new publisher that can only send immediate messages. /// This kind of publisher does not take up a publisher slot. - pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher { + pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<'_, T> { DynImmediatePublisher(ImmediatePub::new(self)) } diff --git a/embassy-usb/src/class/web_usb.rs b/embassy-usb/src/class/web_usb.rs index 405944f14..154b219ca 100644 --- a/embassy-usb/src/class/web_usb.rs +++ b/embassy-usb/src/class/web_usb.rs @@ -84,7 +84,7 @@ impl<'d> Control<'d> { } impl<'d> Handler for Control<'d> { - fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option { + fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option> { let landing_value = if self.landing_url.is_some() { 1 } else { 0 }; if req.request_type == RequestType::Vendor && req.recipient == Recipient::Device diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index e75ea40cc..411cc6946 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-03-12" +channel = "nightly-2025-06-29" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 870904c3a..e24864037 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.85" +channel = "1.88" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", -- cgit From 00b2567fbf6b264a77dbe63ca2424939957f3128 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 30 Jun 2025 00:19:47 +0200 Subject: stm32/dma: add missing fence on BDMA start. --- embassy-stm32/src/dma/dma_bdma.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 7dbbe7b72..caf135989 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -344,6 +344,9 @@ impl AnyChannel { peripheral_size: WordSize, options: TransferOptions, ) { + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + let info = self.info(); #[cfg(feature = "_dual-core")] { @@ -362,9 +365,6 @@ impl AnyChannel { let state: &ChannelState = &STATE[self.id as usize]; let ch = r.st(info.num); - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::SeqCst); - state.complete_count.store(0, Ordering::Release); self.clear_irqs(); -- cgit From 84cc949df649c9b3625a65c2cc14e09155deeede Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 30 Jun 2025 00:18:44 +0200 Subject: stm32/dma: fix packing/unpacking not working. --- embassy-stm32/src/cryp/mod.rs | 13 ++------ embassy-stm32/src/dma/dma_bdma.rs | 63 +++++++++++++++++++++++++-------------- embassy-stm32/src/dma/gpdma.rs | 12 ++++---- embassy-stm32/src/dma/util.rs | 6 ++-- embassy-stm32/src/spi/mod.rs | 2 +- examples/stm32f7/src/bin/cryp.rs | 4 ++- tests/stm32/src/bin/cryp.rs | 2 +- 7 files changed, 57 insertions(+), 45 deletions(-) diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index fba3c0fd7..35d9f8cce 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -2,7 +2,6 @@ #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] use core::cmp::min; use core::marker::PhantomData; -use core::ptr; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; @@ -1814,14 +1813,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { assert_eq!(blocks.len() % block_size, 0); // Configure DMA to transfer input to crypto core. let dst_ptr: *mut u32 = T::regs().din().as_ptr(); - let num_words = blocks.len() / 4; - let src_ptr: *const [u8] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); let options = TransferOptions { #[cfg(not(gpdma))] priority: crate::dma::Priority::High, ..Default::default() }; - let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; + let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) }; T::regs().dmacr().modify(|w| w.set_dien(true)); // Wait for the transfer to complete. dma_transfer.await; @@ -1836,14 +1833,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { assert_eq!((blocks.len() * 4) % block_size, 0); // Configure DMA to transfer input to crypto core. let dst_ptr: *mut u32 = T::regs().din().as_ptr(); - let num_words = blocks.len(); - let src_ptr: *const [u32] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); let options = TransferOptions { #[cfg(not(gpdma))] priority: crate::dma::Priority::High, ..Default::default() }; - let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; + let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) }; T::regs().dmacr().modify(|w| w.set_dien(true)); // Wait for the transfer to complete. dma_transfer.await; @@ -1857,14 +1852,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { assert_eq!(blocks.len() % block_size, 0); // Configure DMA to get output from crypto core. let src_ptr = T::regs().dout().as_ptr(); - let num_words = blocks.len() / 4; - let dst_ptr = ptr::slice_from_raw_parts_mut(blocks.as_mut_ptr().cast(), num_words); let options = TransferOptions { #[cfg(not(gpdma))] priority: crate::dma::Priority::VeryHigh, ..Default::default() }; - let dma_transfer = unsafe { dma.read_raw(src_ptr, dst_ptr, options) }; + let dma_transfer = unsafe { dma.read_raw(src_ptr, blocks, options) }; T::regs().dmacr().modify(|w| w.set_doen(true)); // Wait for the transfer to complete. dma_transfer.await; diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index caf135989..464823bfc 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -341,7 +341,7 @@ impl AnyChannel { mem_len: usize, incr_mem: bool, mem_size: WordSize, - peripheral_size: WordSize, + peri_size: WordSize, options: TransferOptions, ) { // "Preceding reads and writes cannot be moved past subsequent writes." @@ -357,8 +357,6 @@ impl AnyChannel { #[cfg(dmamux)] super::dmamux::configure_dmamux(&info.dmamux, _request); - assert!(mem_len > 0 && mem_len <= 0xFFFF); - match self.info().dma { #[cfg(dma)] DmaInfo::Dma(r) => { @@ -368,14 +366,39 @@ impl AnyChannel { state.complete_count.store(0, Ordering::Release); self.clear_irqs(); + // NDTR is the number of transfers in the *peripheral* word size. + // ex: if mem_size=1, peri_size=4 and ndtr=3 it'll do 12 mem transfers, 3 peri transfers. + let ndtr = match (mem_size, peri_size) { + (WordSize::FourBytes, WordSize::OneByte) => mem_len * 4, + (WordSize::FourBytes, WordSize::TwoBytes) | (WordSize::TwoBytes, WordSize::OneByte) => mem_len * 2, + (WordSize::FourBytes, WordSize::FourBytes) + | (WordSize::TwoBytes, WordSize::TwoBytes) + | (WordSize::OneByte, WordSize::OneByte) => mem_len, + (WordSize::TwoBytes, WordSize::FourBytes) | (WordSize::OneByte, WordSize::TwoBytes) => { + assert!(mem_len % 2 == 0); + mem_len / 2 + } + (WordSize::OneByte, WordSize::FourBytes) => { + assert!(mem_len % 4 == 0); + mem_len / 4 + } + }; + + assert!(ndtr > 0 && ndtr <= 0xFFFF); + ch.par().write_value(peri_addr as u32); ch.m0ar().write_value(mem_addr as u32); - ch.ndtr().write_value(pac::dma::regs::Ndtr(mem_len as _)); + ch.ndtr().write_value(pac::dma::regs::Ndtr(ndtr as _)); ch.fcr().write(|w| { if let Some(fth) = options.fifo_threshold { // FIFO mode w.set_dmdis(pac::dma::vals::Dmdis::DISABLED); w.set_fth(fth.into()); + } else if mem_size != peri_size { + // force FIFO mode if msize != psize + // packing/unpacking doesn't work in direct mode. + w.set_dmdis(pac::dma::vals::Dmdis::DISABLED); + w.set_fth(FifoThreshold::Half.into()); } else { // Direct mode w.set_dmdis(pac::dma::vals::Dmdis::ENABLED); @@ -384,7 +407,7 @@ impl AnyChannel { ch.cr().write(|w| { w.set_dir(dir.into()); w.set_msize(mem_size.into()); - w.set_psize(peripheral_size.into()); + w.set_psize(peri_size.into()); w.set_pl(options.priority.into()); w.set_minc(incr_mem); w.set_pinc(false); @@ -404,6 +427,8 @@ impl AnyChannel { } #[cfg(bdma)] DmaInfo::Bdma(r) => { + assert!(mem_len > 0 && mem_len <= 0xFFFF); + #[cfg(bdma_v2)] critical_section::with(|_| r.cselr().modify(|w| w.set_cs(info.num, _request))); @@ -417,7 +442,7 @@ impl AnyChannel { ch.mar().write_value(mem_addr as u32); ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); ch.cr().write(|w| { - w.set_psize(peripheral_size.into()); + w.set_psize(peri_size.into()); w.set_msize(mem_size.into()); w.set_minc(incr_mem); w.set_dir(dir.into()); @@ -587,11 +612,11 @@ impl<'a> Transfer<'a> { } /// Create a new read DMA transfer (peripheral to memory), using raw pointers. - pub unsafe fn new_read_raw( + pub unsafe fn new_read_raw( channel: Peri<'a, impl Channel>, request: Request, - peri_addr: *mut W, - buf: *mut [W], + peri_addr: *mut PW, + buf: *mut [MW], options: TransferOptions, ) -> Self { Self::new_inner( @@ -599,11 +624,11 @@ impl<'a> Transfer<'a> { request, Dir::PeripheralToMemory, peri_addr as *const u32, - buf as *mut W as *mut u32, + buf as *mut MW as *mut u32, buf.len(), true, - W::size(), - W::size(), + MW::size(), + PW::size(), options, ) } @@ -672,22 +697,14 @@ impl<'a> Transfer<'a> { mem_addr: *mut u32, mem_len: usize, incr_mem: bool, - data_size: WordSize, - peripheral_size: WordSize, + mem_size: WordSize, + peri_size: WordSize, options: TransferOptions, ) -> Self { assert!(mem_len > 0 && mem_len <= 0xFFFF); channel.configure( - _request, - dir, - peri_addr, - mem_addr, - mem_len, - incr_mem, - data_size, - peripheral_size, - options, + _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, ); channel.start(); Self { channel } diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index ade70fb55..151e4ab9f 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -125,11 +125,11 @@ impl<'a> Transfer<'a> { } /// Create a new read DMA transfer (peripheral to memory), using raw pointers. - pub unsafe fn new_read_raw( + pub unsafe fn new_read_raw( channel: Peri<'a, impl Channel>, request: Request, - peri_addr: *mut W, - buf: *mut [W], + peri_addr: *mut PW, + buf: *mut [MW], options: TransferOptions, ) -> Self { Self::new_inner( @@ -137,11 +137,11 @@ impl<'a> Transfer<'a> { request, Dir::PeripheralToMemory, peri_addr as *const u32, - buf as *mut W as *mut u32, + buf as *mut MW as *mut u32, buf.len(), true, - W::size(), - W::size(), + PW::size(), + MW::size(), options, ) } diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 8bf89e2fe..3245887c1 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs @@ -20,10 +20,10 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) } - pub unsafe fn read_raw<'a, W: Word>( + pub unsafe fn read_raw<'a, MW: Word, PW: Word>( &'a mut self, - peri_addr: *mut W, - buf: *mut [W], + peri_addr: *mut PW, + buf: *mut [MW], options: TransferOptions, ) -> Transfer<'a> { Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 9e2ba093a..c8d83f07e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -843,7 +843,7 @@ impl<'d> Spi<'d, Async> { set_rxdmaen(self.info.regs, true); - let rx_src = self.info.regs.rx_ptr(); + let rx_src = self.info.regs.rx_ptr::(); let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; let tx_dst: *mut W = self.info.regs.tx_ptr(); diff --git a/examples/stm32f7/src/bin/cryp.rs b/examples/stm32f7/src/bin/cryp.rs index 235853cb9..a31e9b4f2 100644 --- a/examples/stm32f7/src/bin/cryp.rs +++ b/examples/stm32f7/src/bin/cryp.rs @@ -68,7 +68,9 @@ async fn main(_spawner: Spawner) -> ! { ); // Decrypt in software using AES-GCM 128-bit - let _ = cipher.decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec); + cipher + .decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec) + .unwrap(); let sw_end_time = Instant::now(); let sw_execution_time = sw_end_time - sw_start_time; diff --git a/tests/stm32/src/bin/cryp.rs b/tests/stm32/src/bin/cryp.rs index 028775ac8..f54c99cc3 100644 --- a/tests/stm32/src/bin/cryp.rs +++ b/tests/stm32/src/bin/cryp.rs @@ -72,7 +72,7 @@ async fn main(_spawner: Spawner) { defmt::assert!(encrypt_tag == payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]); // Decrypt in software using AES-GCM 128-bit - let _ = cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec); + cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec).unwrap(); info!("Test OK"); cortex_m::asm::bkpt(); -- cgit From 3127e1c50b2ea2efddba199ae780cb5ebb571c00 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 30 Jun 2025 04:02:09 +0200 Subject: sdmmc: use div_ceil. --- embassy-stm32/src/sdmmc/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 675d1813b..c82407334 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -225,8 +225,7 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { return Ok((true, 0, ker_ck)); } - // `ker_ck / sdmmc_ck` rounded up - let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { + let clk_div = match ker_ck.0.div_ceil(sdmmc_ck) { 0 | 1 => Ok(0), x @ 2..=258 => Ok((x - 2) as u8), _ => Err(Error::BadClock), @@ -244,12 +243,11 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. #[cfg(sdmmc_v2)] fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { - // `ker_ck / sdmmc_ck` rounded up - match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { + match ker_ck.0.div_ceil(sdmmc_ck) { 0 | 1 => Ok((false, 0, ker_ck)), x @ 2..=2046 => { // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] - let clk_div = ((x + 1) / 2) as u16; + let clk_div = x.div_ceil(2) as u16; let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); Ok((false, clk_div, clk)) -- cgit From c8a4a4995844be1b61d1a1479a6009eeb69b1117 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 4 Jul 2025 00:25:00 +0200 Subject: stm32/sdmmc: misc improvements --- embassy-stm32/src/sdmmc/mod.rs | 124 ++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 71 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index c82407334..6e5d735d7 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -32,25 +32,48 @@ pub struct InterruptHandler { } impl InterruptHandler { - fn data_interrupts(enable: bool) { + fn enable_interrupts() { let regs = T::regs(); regs.maskr().write(|w| { - w.set_dcrcfailie(enable); - w.set_dtimeoutie(enable); - w.set_dataendie(enable); + w.set_dcrcfailie(true); + w.set_dtimeoutie(true); + w.set_dataendie(true); + w.set_dbckendie(true); #[cfg(sdmmc_v1)] - w.set_stbiterre(enable); + w.set_stbiterre(true); #[cfg(sdmmc_v2)] - w.set_dabortie(enable); + w.set_dabortie(true); }); } } impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - Self::data_interrupts(false); T::state().wake(); + let status = T::regs().star().read(); + T::regs().maskr().modify(|w| { + if status.dcrcfail() { + w.set_dcrcfailie(false) + } + if status.dtimeout() { + w.set_dtimeoutie(false) + } + if status.dataend() { + w.set_dataendie(false) + } + if status.dbckend() { + w.set_dbckendie(false) + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + w.set_stbiterre(false) + } + #[cfg(sdmmc_v2)] + if status.dabort() { + w.set_dabortie(false) + } + }); } } @@ -1002,14 +1025,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Wait for the abort while Self::data_active() {} } - InterruptHandler::::data_interrupts(false); + regs.maskr().write(|_| ()); // disable irqs Self::clear_interrupt_flags(); Self::stop_datapath(); } /// Wait for a previously started datapath transfer to complete from an interrupt. #[inline] - async fn complete_datapath_transfer() -> Result<(), Error> { + async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { let regs = T::regs(); let res = poll_fn(|cx| { @@ -1029,7 +1052,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { if status.stbiterr() { return Poll::Ready(Err(Error::StBitErr)); } - if status.dataend() { + let done = match block { + true => status.dbckend(), + false => status.dataend(), + }; + if done { return Poll::Ready(Ok(())); } Poll::Pending @@ -1067,10 +1094,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 512, 9, ); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); Self::cmd(common_cmd::read_single_block(address), true)?; - let res = Self::complete_datapath_transfer().await; + let res = Self::complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); @@ -1100,7 +1127,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }; Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = Self::prepare_datapath_read( @@ -1111,30 +1137,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 512 * blocks.len() as u32, 9, ); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); Self::cmd(common_cmd::read_multiple_blocks(address), true)?; - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; + let res = Self::complete_datapath_transfer(false).await; Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 Self::clear_interrupt_flags(); @@ -1169,12 +1176,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Self::cmd(common_cmd::write_single_block(address), true)?; let transfer = self.prepare_datapath_write(buffer, 512, 9); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); #[cfg(sdmmc_v2)] Self::cmd(common_cmd::write_single_block(address), true)?; - let res = Self::complete_datapath_transfer().await; + let res = Self::complete_datapath_transfer(true).await; match res { Ok(_) => { @@ -1225,7 +1232,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let block_count = blocks.len(); - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); #[cfg(sdmmc_v1)] @@ -1233,36 +1239,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Setup write command let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); #[cfg(sdmmc_v2)] Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - if status.txunderr() { - return Poll::Ready(Err(Error::Underrun)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - - Poll::Pending - }) - .await; + let res = Self::complete_datapath_transfer(false).await; Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 Self::clear_interrupt_flags(); @@ -1597,10 +1579,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 64, 6, ); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - let res = Self::complete_datapath_transfer().await; + let res = Self::complete_datapath_transfer(true).await; // Host is allowed to use the new functions at least 8 // clocks after the end of the switch command @@ -1657,10 +1639,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 8, 3, ); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); Self::cmd(sd_cmd::send_scr(), true)?; - let res = Self::complete_datapath_transfer().await; + let res = Self::complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); @@ -1703,10 +1685,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 64, 6, ); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); Self::cmd(sd_cmd::sd_status(), true)?; - let res = Self::complete_datapath_transfer().await; + let res = Self::complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); @@ -1753,10 +1735,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 512, 9, ); - InterruptHandler::::data_interrupts(true); + InterruptHandler::::enable_interrupts(); Self::cmd(emmc_cmd::send_ext_csd(), true)?; - let res = Self::complete_datapath_transfer().await; + let res = Self::complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); -- cgit From a29267752a382ff52b052233586ef9007fe84fed Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 4 Jul 2025 00:26:41 +0200 Subject: stm32/sdmmc: disable 1bit test. --- tests/stm32/src/bin/sdmmc.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 34a53a725..9f9c526e1 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -95,6 +95,9 @@ async fn main(_spawner: Spawner) { drop(s); + // FIXME: this hangs on Rust 1.86 and higher. + // I haven't been able to figure out why. + /* // ======== Try 1bit. ============== info!("initializing in 1-bit mode..."); let mut s = Sdmmc::new_1bit( @@ -151,6 +154,7 @@ async fn main(_spawner: Spawner) { assert_eq!(&blocks, &patterns); drop(s); + */ info!("Test OK"); cortex_m::asm::bkpt(); -- cgit From fb21fcf4f1d2acfa15cf4da03fff190c0bb9df59 Mon Sep 17 00:00:00 2001 From: Cristian Milatinov Date: Sat, 5 Jul 2025 00:47:30 -0400 Subject: Added sample shifting to qspi config for stm32 --- embassy-stm32/src/qspi/enums.rs | 16 ++++++++++++++++ embassy-stm32/src/qspi/mod.rs | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs index 9ec4c1b43..3905fcbf8 100644 --- a/embassy-stm32/src/qspi/enums.rs +++ b/embassy-stm32/src/qspi/enums.rs @@ -331,3 +331,19 @@ impl From for u8 { } } } + +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum SampleShifting { + None, + HalfCycle +} + +impl From for bool { + fn from(value: SampleShifting) -> Self { + match value { + SampleShifting::None => false, + SampleShifting::HalfCycle => true + } + } +} \ No newline at end of file diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 0df057c53..52b1f3084 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -58,6 +58,8 @@ pub struct Config { pub fifo_threshold: FIFOThresholdLevel, /// Minimum number of cycles that chip select must be high between issued commands pub cs_high_time: ChipSelectHighTime, + /// Shift sampling point of input data (none, or half-cycle) + pub sample_shifting: SampleShifting, } impl Default for Config { @@ -68,6 +70,7 @@ impl Default for Config { prescaler: 128, fifo_threshold: FIFOThresholdLevel::_17Bytes, cs_high_time: ChipSelectHighTime::_5Cycle, + sample_shifting: SampleShifting::None } } } @@ -120,7 +123,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { T::REGS.cr().modify(|w| { w.set_en(true); //w.set_tcen(false); - w.set_sshift(false); + w.set_sshift(config.sample_shifting.into()); w.set_fthres(config.fifo_threshold.into()); w.set_prescaler(config.prescaler); w.set_fsel(fsel.into()); -- cgit From 1f87e4783110cb752f364657ae7c2759d6a2ac27 Mon Sep 17 00:00:00 2001 From: Cristian Milatinov Date: Sat, 5 Jul 2025 01:15:15 -0400 Subject: Run cargo fmt --- embassy-stm32/src/qspi/enums.rs | 6 +++--- embassy-stm32/src/qspi/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs index 3905fcbf8..fa5e36d06 100644 --- a/embassy-stm32/src/qspi/enums.rs +++ b/embassy-stm32/src/qspi/enums.rs @@ -336,14 +336,14 @@ impl From for u8 { #[derive(Copy, Clone)] pub enum SampleShifting { None, - HalfCycle + HalfCycle, } impl From for bool { fn from(value: SampleShifting) -> Self { match value { SampleShifting::None => false, - SampleShifting::HalfCycle => true + SampleShifting::HalfCycle => true, } } -} \ No newline at end of file +} diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 52b1f3084..1e20d7cd3 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -70,7 +70,7 @@ impl Default for Config { prescaler: 128, fifo_threshold: FIFOThresholdLevel::_17Bytes, cs_high_time: ChipSelectHighTime::_5Cycle, - sample_shifting: SampleShifting::None + sample_shifting: SampleShifting::None, } } } -- cgit From 9134fb2dd4f8430b1630cff98a93f030dae3ebeb Mon Sep 17 00:00:00 2001 From: Cristian Milatinov Date: Sat, 5 Jul 2025 01:32:21 -0400 Subject: Update examples to add SampleShifting in qspi config --- examples/stm32f7/src/bin/qspi.rs | 1 + examples/stm32h742/src/bin/qspi.rs | 1 + examples/stm32l432/src/bin/qspi_mmap.rs | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index bd3287964..ab29ddeff 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs @@ -279,6 +279,7 @@ async fn main(_spawner: Spawner) -> ! { prescaler: 16, cs_high_time: ChipSelectHighTime::_1Cycle, fifo_threshold: FIFOThresholdLevel::_16Bytes, + sample_shifting: SampleShifting::None, }; let driver = Qspi::new_bank1( p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs index aee07f3f2..50e37ec52 100644 --- a/examples/stm32h742/src/bin/qspi.rs +++ b/examples/stm32h742/src/bin/qspi.rs @@ -272,6 +272,7 @@ async fn main(_spawner: Spawner) -> ! { prescaler: 16, cs_high_time: ChipSelectHighTime::_1Cycle, fifo_threshold: FIFOThresholdLevel::_16Bytes, + sample_shifting: SampleShifting::None, }; let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); let mut flash = FlashMemory::new(driver); diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs index 86a20eb3d..414621475 100644 --- a/examples/stm32l432/src/bin/qspi_mmap.rs +++ b/examples/stm32l432/src/bin/qspi_mmap.rs @@ -7,7 +7,8 @@ use defmt::info; use embassy_stm32::mode; use embassy_stm32::qspi::enums::{ - AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, + AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, + SampleShifting }; use embassy_stm32::qspi::{self, Instance, TransferConfig}; pub struct FlashMemory { @@ -252,6 +253,7 @@ async fn main(_spawner: Spawner) { prescaler: 200, cs_high_time: ChipSelectHighTime::_1Cycle, fifo_threshold: FIFOThresholdLevel::_16Bytes, + sample_shifting: SampleShifting::None, }; let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); let mut flash = FlashMemory::new(driver); -- cgit From bd5b1580dfb07e0bcd5ec1d0e5f8cc8b958d72ba Mon Sep 17 00:00:00 2001 From: Cristian Milatinov Date: Sat, 5 Jul 2025 01:34:10 -0400 Subject: Run cargo fmt --- examples/stm32l432/src/bin/qspi_mmap.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs index 414621475..075458fe5 100644 --- a/examples/stm32l432/src/bin/qspi_mmap.rs +++ b/examples/stm32l432/src/bin/qspi_mmap.rs @@ -7,8 +7,7 @@ use defmt::info; use embassy_stm32::mode; use embassy_stm32::qspi::enums::{ - AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, - SampleShifting + AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, SampleShifting, }; use embassy_stm32::qspi::{self, Instance, TransferConfig}; pub struct FlashMemory { -- cgit From bfbecdf93acd6f05f9d02477729defbd84a9bdeb Mon Sep 17 00:00:00 2001 From: Thomas Giesel Date: Sun, 29 Jun 2025 21:28:26 +0200 Subject: Use proper RCC clock enable for opamps new() now resets the opamp and enables its clock. The clock is disabled when the opamp is dropped. On families that use SYSCFGEN (F3 and G4), this is not done because this clock is always on in Embassy. This change makes use of the RCC driver, which uses a reference counter to prevent conflicts. The opamp itself is still disabled when its output is dropped. --- embassy-stm32/src/opamp.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index 0467dbce3..e36719ef3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -4,6 +4,8 @@ use embassy_hal_internal::PeripheralType; use crate::pac::opamp::vals::*; +#[cfg(not(any(stm32g4, stm32f3)))] +use crate::rcc::RccInfo; use crate::Peri; /// Performs a busy-wait delay for a specified number of microseconds. @@ -68,6 +70,8 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// /// Does not enable the opamp, but does set the speed mode on some families. pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_v5)] speed: OpAmpSpeed) -> Self { + #[cfg(not(any(stm32g4, stm32f3)))] + T::info().rcc.enable_and_reset(); #[cfg(opamp_v5)] T::regs().csr().modify(|w| { w.set_opahsm(speed == OpAmpSpeed::HighSpeed); @@ -452,6 +456,13 @@ impl<'d, T: Instance> OpAmp<'d, T> { } } +#[cfg(not(any(stm32g4, stm32f3)))] +impl<'d, T: Instance> Drop for OpAmp<'d, T> { + fn drop(&mut self) { + T::info().rcc.disable(); + } +} + impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { fn drop(&mut self) { T::regs().csr().modify(|w| { @@ -469,7 +480,14 @@ impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { } } +#[cfg(not(any(stm32g4, stm32f3)))] +pub(crate) struct Info { + rcc: RccInfo, +} + pub(crate) trait SealedInstance { + #[cfg(not(any(stm32g4, stm32f3)))] + fn info() -> &'static Info; fn regs() -> crate::pac::opamp::Opamp; } @@ -600,6 +618,15 @@ foreach_peripheral!( foreach_peripheral! { (opamp, $inst:ident) => { impl SealedInstance for crate::peripherals::$inst { + // G4 and F3 use SYSCFGEN, which is always enabled + #[cfg(not(any(stm32g4, stm32f3)))] + fn info() -> &'static Info { + use crate::rcc::SealedRccPeripheral; + static INFO: Info = Info { + rcc: crate::peripherals::$inst::RCC_INFO, + }; + &INFO + } fn regs() -> crate::pac::opamp::Opamp { crate::pac::$inst } -- cgit From b861dd172829c5b34e95644287544e090dd9f568 Mon Sep 17 00:00:00 2001 From: Florian Grandel Date: Sat, 5 Jul 2025 18:27:46 +0200 Subject: embassy-executor: rtos-trace: fix task naming for new tasks Tasks that are spawned after starting SystemViewer were not named. This change ensures that tasks spawned while SystemViewer is running will be properly named, too. Signed-off-by: Florian Grandel --- embassy-executor/src/raw/trace.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index 6c9cfda25..aa27ab37e 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -283,7 +283,17 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { } #[cfg(feature = "rtos-trace")] - rtos_trace::trace::task_new(task.as_ptr() as u32); + { + rtos_trace::trace::task_new(task.as_ptr() as u32); + let name = task.name().unwrap_or("unnamed task\0"); + let info = rtos_trace::TaskInfo { + name, + priority: 0, + stack_base: 0, + stack_size: 0, + }; + rtos_trace::trace::task_send_info(task.id(), info); + } #[cfg(feature = "rtos-trace")] TASK_TRACKER.add(*task); -- cgit From e57dffafa5723931dd529afe8e22cba0c9ea09f0 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Mon, 23 Jun 2025 23:15:09 -0500 Subject: mspm0: add dma driver --- embassy-mspm0/Cargo.toml | 4 +- embassy-mspm0/build.rs | 21 +- embassy-mspm0/src/dma.rs | 626 +++++++++++++++++++++++++++++++++ embassy-mspm0/src/gpio.rs | 18 +- embassy-mspm0/src/lib.rs | 115 +++++- examples/mspm0g3507/.cargo/config.toml | 2 +- tests/mspm0/Cargo.toml | 1 + tests/mspm0/build.rs | 3 + tests/mspm0/memory_g3519.x | 6 + tests/mspm0/src/bin/dma.rs | 503 ++++++++++++++++++++++++++ tests/mspm0/src/bin/uart.rs | 5 +- 11 files changed, 1279 insertions(+), 25 deletions(-) create mode 100644 embassy-mspm0/src/dma.rs create mode 100644 tests/mspm0/memory_g3519.x create mode 100644 tests/mspm0/src/bin/dma.rs diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index 6f767a3c0..550b037c1 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -46,14 +46,14 @@ cortex-m = "0.7.6" critical-section = "1.2.0" # mspm0-metapac = { version = "" } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-26a6f681eda4ef120e8cb614a1631727c848590f" } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f" } [build-dependencies] proc-macro2 = "1.0.94" quote = "1.0.40" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-26a6f681eda4ef120e8cb614a1631727c848590f", default-features = false, features = ["metadata"] } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 6cd62895b..b9ba3aecf 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -67,6 +67,7 @@ fn generate_code() { g.extend(generate_peripheral_instances()); g.extend(generate_pin_trait_impls()); g.extend(generate_groups()); + g.extend(generate_dma_channel_count()); let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); @@ -209,6 +210,12 @@ fn generate_groups() -> TokenStream { } } +fn generate_dma_channel_count() -> TokenStream { + let count = METADATA.dma_channels.len(); + + quote! { pub const DMA_CHANNELS: usize = #count; } +} + #[derive(Debug, Clone)] struct Singleton { name: String, @@ -543,8 +550,6 @@ fn generate_peripheral_instances() -> TokenStream { for peripheral in METADATA.peripherals { let peri = format_ident!("{}", peripheral.name); - // Will be filled in when uart implementation is finished - let _ = peri; let tokens = match peripheral.kind { "uart" => Some(quote! { impl_uart_instance!(#peri); }), _ => None, @@ -555,6 +560,18 @@ fn generate_peripheral_instances() -> TokenStream { } } + // DMA channels + for dma_channel in METADATA.dma_channels.iter() { + let peri = format_ident!("DMA_CH{}", dma_channel.number); + let num = dma_channel.number; + + if dma_channel.full { + impls.push(quote! { impl_full_dma_channel!(#peri, #num); }); + } else { + impls.push(quote! { impl_dma_channel!(#peri, #num); }); + } + } + quote! { #(#impls)* } diff --git a/embassy-mspm0/src/dma.rs b/embassy-mspm0/src/dma.rs new file mode 100644 index 000000000..66b79709c --- /dev/null +++ b/embassy-mspm0/src/dma.rs @@ -0,0 +1,626 @@ +//! Direct Memory Access (DMA) + +#![macro_use] + +use core::future::Future; +use core::mem; +use core::pin::Pin; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; + +use critical_section::CriticalSection; +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +use mspm0_metapac::common::{Reg, RW}; +use mspm0_metapac::dma::regs; +use mspm0_metapac::dma::vals::{self, Autoen, Em, Incr, Preirq, Wdth}; + +use crate::{interrupt, pac, Peri}; + +/// The burst size of a DMA transfer. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BurstSize { + /// The whole block transfer is completed in one transfer without interruption. + Complete, + + /// The burst size is 8, after 9 transfers the block transfer is interrupted and the priority + /// is reevaluated. + _8, + + /// The burst size is 16, after 17 transfers the block transfer is interrupted and the priority + /// is reevaluated. + _16, + + /// The burst size is 32, after 32 transfers the block transfer is interrupted and the priority + /// is reevaluated. + _32, +} + +/// DMA channel. +#[allow(private_bounds)] +pub trait Channel: Into + PeripheralType {} + +/// Full DMA channel. +#[allow(private_bounds)] +pub trait FullChannel: Channel + Into {} + +/// Type-erased DMA channel. +pub struct AnyChannel { + pub(crate) id: u8, +} +impl_peripheral!(AnyChannel); + +impl SealedChannel for AnyChannel { + fn id(&self) -> u8 { + self.id + } +} +impl Channel for AnyChannel {} + +/// Type-erased full DMA channel. +pub struct AnyFullChannel { + pub(crate) id: u8, +} +impl_peripheral!(AnyFullChannel); + +impl SealedChannel for AnyFullChannel { + fn id(&self) -> u8 { + self.id + } +} +impl Channel for AnyFullChannel {} +impl FullChannel for AnyFullChannel {} + +impl From for AnyChannel { + fn from(value: AnyFullChannel) -> Self { + Self { id: value.id } + } +} + +#[allow(private_bounds)] +pub trait Word: SealedWord { + /// Size in bytes for the width. + fn size() -> isize; +} + +impl SealedWord for u8 { + fn width() -> vals::Wdth { + vals::Wdth::BYTE + } +} +impl Word for u8 { + fn size() -> isize { + 1 + } +} + +impl SealedWord for u16 { + fn width() -> vals::Wdth { + vals::Wdth::HALF + } +} +impl Word for u16 { + fn size() -> isize { + 2 + } +} + +impl SealedWord for u32 { + fn width() -> vals::Wdth { + vals::Wdth::WORD + } +} +impl Word for u32 { + fn size() -> isize { + 4 + } +} + +impl SealedWord for u64 { + fn width() -> vals::Wdth { + vals::Wdth::LONG + } +} +impl Word for u64 { + fn size() -> isize { + 8 + } +} + +// TODO: u128 (LONGLONG) support. G350x does support it, but other parts do not such as C110x. More metadata is +// needed to properly enable this. +// impl SealedWord for u128 { +// fn width() -> vals::Wdth { +// vals::Wdth::LONGLONG +// } +// } +// impl Word for u128 { +// fn size() -> isize { +// 16 +// } +// } + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The DMA transfer is too large. + /// + /// The hardware limits the DMA to 16384 transfers per channel at a time. This means that transferring + /// 16384 `u8` and 16384 `u64` are equivalent, since the DMA must copy 16384 values. + TooManyTransfers, +} + +/// DMA transfer mode for basic channels. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TransferMode { + /// Each DMA trigger will transfer a single value. + Single, + + /// Each DMA trigger will transfer the complete block with one trigger. + Block, +} + +/// DMA transfer options. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions { + /// DMA transfer mode. + pub mode: TransferMode, + // TODO: Read and write stride. +} + +impl Default for TransferOptions { + fn default() -> Self { + Self { + mode: TransferMode::Single, + } + } +} + +/// DMA transfer. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a> { + channel: Peri<'a, AnyChannel>, +} + +impl<'a> Transfer<'a> { + /// Software trigger source. + /// + /// Using this trigger source means that a transfer will start immediately rather than waiting for + /// a hardware event. This can be useful if you want to do a DMA accelerated memcpy. + pub const SOFTWARE_TRIGGER: u8 = 0; + + /// Create a new read DMA transfer. + pub unsafe fn new_read( + channel: Peri<'a, impl Channel>, + trigger_source: u8, + src: *mut SW, + dst: &'a mut [DW], + options: TransferOptions, + ) -> Result { + Self::new_read_raw(channel, trigger_source, src, dst, options) + } + + /// Create a new read DMA transfer, using raw pointers. + pub unsafe fn new_read_raw( + channel: Peri<'a, impl Channel>, + trigger_source: u8, + src: *mut SW, + dst: *mut [DW], + options: TransferOptions, + ) -> Result { + verify_transfer::(dst)?; + + let channel = channel.into(); + channel.configure( + trigger_source, + src.cast(), + SW::width(), + dst.cast(), + DW::width(), + dst.len() as u16, + false, + true, + options, + ); + channel.start(); + + Ok(Self { channel }) + } + + /// Create a new write DMA transfer. + pub unsafe fn new_write( + channel: Peri<'a, impl Channel>, + trigger_source: u8, + src: &'a [SW], + dst: *mut DW, + options: TransferOptions, + ) -> Result { + Self::new_write_raw(channel, trigger_source, src, dst, options) + } + + /// Create a new write DMA transfer, using raw pointers. + pub unsafe fn new_write_raw( + channel: Peri<'a, impl Channel>, + trigger_source: u8, + src: *const [SW], + dst: *mut DW, + options: TransferOptions, + ) -> Result { + verify_transfer::(src)?; + + let channel = channel.into(); + channel.configure( + trigger_source, + src.cast(), + SW::width(), + dst.cast(), + DW::width(), + src.len() as u16, + true, + false, + options, + ); + channel.start(); + + Ok(Self { channel }) + } + + // TODO: Copy between slices. + + /// Request the transfer to resume. + pub fn resume(&mut self) { + self.channel.resume(); + } + + /// Request the transfer to pause, keeping the existing configuration for this channel. + /// To restart the transfer, call [`start`](Self::start) again. + /// + /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. + pub fn request_pause(&mut self) { + self.channel.request_pause(); + } + + /// Return whether this transfer is still running. + /// + /// If this returns [`false`], it can be because either the transfer finished, or + /// it was requested to stop early with [`request_stop`]. + pub fn is_running(&mut self) -> bool { + self.channel.is_running() + } + + /// Blocking wait until the transfer finishes. + pub fn blocking_wait(mut self) { + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + // Prevent drop from being called since we ran to completion (drop will try to pause). + mem::forget(self); + } +} + +impl<'a> Unpin for Transfer<'a> {} +impl<'a> Future for Transfer<'a> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let state: &ChannelState = &STATE[self.channel.id as usize]; + + state.waker.register(cx.waker()); + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + if self.channel.is_running() { + Poll::Pending + } else { + Poll::Ready(()) + } + } +} + +impl<'a> Drop for Transfer<'a> { + fn drop(&mut self) { + self.channel.request_pause(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + } +} + +// impl details + +fn verify_transfer(ptr: *const [W]) -> Result<(), Error> { + if ptr.len() > (u16::MAX as usize) { + return Err(Error::TooManyTransfers); + } + + // TODO: Stride checks + + Ok(()) +} + +fn convert_burst_size(value: BurstSize) -> vals::Burstsz { + match value { + BurstSize::Complete => vals::Burstsz::INFINITI, + BurstSize::_8 => vals::Burstsz::BURST_8, + BurstSize::_16 => vals::Burstsz::BURST_16, + BurstSize::_32 => vals::Burstsz::BURST_32, + } +} + +fn convert_mode(mode: TransferMode) -> vals::Tm { + match mode { + TransferMode::Single => vals::Tm::SINGLE, + TransferMode::Block => vals::Tm::BLOCK, + } +} + +const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS; +static STATE: [ChannelState; CHANNEL_COUNT] = [const { ChannelState::new() }; CHANNEL_COUNT]; + +struct ChannelState { + waker: AtomicWaker, +} + +impl ChannelState { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } +} + +/// SAFETY: Must only be called once. +/// +/// Changing the burst size mid transfer may have some odd behavior. +pub(crate) unsafe fn init(_cs: CriticalSection, burst_size: BurstSize, round_robin: bool) { + pac::DMA.prio().modify(|prio| { + prio.set_burstsz(convert_burst_size(burst_size)); + prio.set_roundrobin(round_robin); + }); + pac::DMA.int_event(0).imask().modify(|w| { + w.set_dataerr(true); + w.set_addrerr(true); + }); + + interrupt::DMA.enable(); +} + +pub(crate) trait SealedWord { + fn width() -> vals::Wdth; +} + +pub(crate) trait SealedChannel { + fn id(&self) -> u8; + + #[inline] + fn tctl(&self) -> Reg { + pac::DMA.trig(self.id() as usize).tctl() + } + + #[inline] + fn ctl(&self) -> Reg { + pac::DMA.chan(self.id() as usize).ctl() + } + + #[inline] + fn sa(&self) -> Reg { + pac::DMA.chan(self.id() as usize).sa() + } + + #[inline] + fn da(&self) -> Reg { + pac::DMA.chan(self.id() as usize).da() + } + + #[inline] + fn sz(&self) -> Reg { + pac::DMA.chan(self.id() as usize).sz() + } + + #[inline] + fn mask_interrupt(&self, enable: bool) { + // Enabling interrupts is an RMW operation. + critical_section::with(|_cs| { + pac::DMA.int_event(0).imask().modify(|w| { + w.set_ch(self.id() as usize, enable); + }); + }) + } + + /// # Safety + /// + /// - `src` must be valid for the lifetime of the transfer. + /// - `dst` must be valid for the lifetime of the transfer. + unsafe fn configure( + &self, + trigger_sel: u8, + src: *const u32, + src_wdth: Wdth, + dst: *const u32, + dst_wdth: Wdth, + transfer_count: u16, + increment_src: bool, + increment_dst: bool, + options: TransferOptions, + ) { + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + self.ctl().modify(|w| { + // SLAU 5.2.5: + // "The DMATSEL bits should be modified only when the DMACTLx.DMAEN bit is + // 0; otherwise, unpredictable DMA triggers can occur." + // + // We also want to stop any transfers before setup. + w.set_en(false); + w.set_req(false); + + // Not every part supports auto enable, so force its value to 0. + w.set_autoen(Autoen::NONE); + w.set_preirq(Preirq::PREIRQ_DISABLE); + w.set_srcwdth(src_wdth); + w.set_dstwdth(dst_wdth); + w.set_srcincr(if increment_src { + Incr::INCREMENT + } else { + Incr::UNCHANGED + }); + w.set_dstincr(if increment_dst { + Incr::INCREMENT + } else { + Incr::UNCHANGED + }); + + w.set_em(Em::NORMAL); + // Single and block will clear the enable bit when the transfers finish. + w.set_tm(convert_mode(options.mode)); + }); + + self.tctl().write(|w| { + w.set_tsel(trigger_sel); + // Basic channels do not implement cross triggering. + w.set_tint(vals::Tint::EXTERNAL); + }); + + self.sz().write(|w| { + w.set_size(transfer_count); + }); + + self.sa().write_value(src as u32); + self.da().write_value(dst as u32); + + // Enable the channel. + self.ctl().modify(|w| { + // FIXME: Why did putting set_req later fix some transfers + w.set_en(true); + w.set_req(true); + }); + } + + fn start(&self) { + self.mask_interrupt(true); + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + // Request the DMA transfer to start. + self.ctl().modify(|w| { + w.set_req(true); + }); + } + + fn resume(&self) { + self.mask_interrupt(true); + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + self.ctl().modify(|w| { + // w.set_en(true); + w.set_req(true); + }); + } + + fn request_pause(&self) { + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + // Stop the transfer. + // + // SLAU846 5.2.6: + // "A DMA block transfer in progress can be stopped by clearing the DMAEN bit" + self.ctl().modify(|w| { + // w.set_en(false); + w.set_req(false); + }); + } + + fn is_running(&self) -> bool { + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::SeqCst); + + let ctl = self.ctl().read(); + + // Is the transfer requested? + ctl.req() + // Is the channel enabled? + && ctl.en() + } +} + +macro_rules! impl_dma_channel { + ($instance: ident, $num: expr) => { + impl crate::dma::SealedChannel for crate::peripherals::$instance { + fn id(&self) -> u8 { + $num + } + } + + impl From for crate::dma::AnyChannel { + fn from(value: crate::peripherals::$instance) -> Self { + use crate::dma::SealedChannel; + + Self { id: value.id() } + } + } + + impl crate::dma::Channel for crate::peripherals::$instance {} + }; +} + +// C1104 has no full DMA channels. +#[allow(unused_macros)] +macro_rules! impl_full_dma_channel { + ($instance: ident, $num: expr) => { + impl_dma_channel!($instance, $num); + + impl From for crate::dma::AnyFullChannel { + fn from(value: crate::peripherals::$instance) -> Self { + use crate::dma::SealedChannel; + + Self { id: value.id() } + } + } + + impl crate::dma::FullChannel for crate::peripherals::$instance {} + }; +} + +#[cfg(feature = "rt")] +#[interrupt] +fn DMA() { + use crate::BitIter; + + let events = pac::DMA.int_event(0); + let mis = events.mis().read(); + + // TODO: Handle DATAERR and ADDRERR? However we do not know which channel causes an error. + if mis.dataerr() { + panic!("DMA data error"); + } else if mis.addrerr() { + panic!("DMA address error") + } + + // Ignore preirq interrupts (values greater than 16). + for i in BitIter(mis.0 & 0x0000_FFFF) { + if let Some(state) = STATE.get(i as usize) { + state.waker.wake(); + + // Notify the future that the counter size hit zero + events.imask().modify(|w| { + w.set_ch(i as usize, false); + }); + } + } +} diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index 738d51928..e6380e819 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs @@ -1090,7 +1090,9 @@ pub(crate) fn init(gpio: gpio::Gpio) { #[cfg(feature = "rt")] fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { + use crate::BitIter; // Only consider pins which have interrupts unmasked. + let bits = gpio.cpu_int().mis().read().0; for i in BitIter(bits) { @@ -1103,22 +1105,6 @@ fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { } } -struct BitIter(u32); - -impl Iterator for BitIter { - type Item = u32; - - fn next(&mut self) -> Option { - match self.0.trailing_zeros() { - 32 => None, - b => { - self.0 &= !(1 << b); - Some(b) - } - } - } -} - // C110x and L110x have a dedicated interrupts just for GPIOA. // // These chips do not have a GROUP1 interrupt. diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 7ff60e946..bb8d91403 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -13,6 +13,7 @@ pub(crate) mod fmt; // This must be declared early as well for mod macros; +pub mod dma; pub mod gpio; pub mod timer; pub mod uart; @@ -59,22 +60,106 @@ pub(crate) use mspm0_metapac as pac; pub use crate::_generated::interrupt; +/// Macro to bind interrupts to handlers. +/// +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +/// +/// Example of how to bind one interrupt: +/// +/// ```rust,ignore +/// use embassy_nrf::{bind_interrupts, spim, peripherals}; +/// +/// bind_interrupts!( +/// /// Binds the SPIM3 interrupt. +/// struct Irqs { +/// SPIM3 => spim::InterruptHandler; +/// } +/// ); +/// ``` +/// +/// Example of how to bind multiple interrupts in a single macro invocation: +/// +/// ```rust,ignore +/// use embassy_nrf::{bind_interrupts, spim, twim, peripherals}; +/// +/// bind_interrupts!(struct Irqs { +/// SPIM3 => spim::InterruptHandler; +/// TWISPI0 => twim::InterruptHandler; +/// }); +/// ``` + +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. +#[macro_export] +macro_rules! bind_interrupts { + ($(#[$attr:meta])* $vis:vis struct $name:ident { + $( + $(#[cfg($cond_irq:meta)])? + $irq:ident => $( + $(#[cfg($cond_handler:meta)])? + $handler:ty + ),*; + )* + }) => { + #[derive(Copy, Clone)] + $(#[$attr])* + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + $(#[cfg($cond_irq)])? + unsafe extern "C" fn $irq() { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + + )* + } + + $(#[cfg($cond_irq)])? + $crate::bind_interrupts!(@inner + $( + $(#[cfg($cond_handler)])? + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} + )* + ); + )* + }; + (@inner $($t:tt)*) => { + $($t)* + } +} + /// `embassy-mspm0` global configuration. #[non_exhaustive] #[derive(Clone, Copy)] pub struct Config { - // TODO + // TODO: OSC configuration. + /// The size of DMA block transfer burst. + /// + /// If this is set to a value + pub dma_burst_size: dma::BurstSize, + + /// Whether the DMA channels are used in a fixed priority or a round robin fashion. + /// + /// If [`false`], the DMA priorities are fixed. + /// + /// If [`true`], after a channel finishes a transfer it becomes the lowest priority. + pub dma_round_robin: bool, } impl Default for Config { fn default() -> Self { Self { - // TODO + dma_burst_size: dma::BurstSize::Complete, + dma_round_robin: false, } } } -pub fn init(_config: Config) -> Peripherals { +pub fn init(config: Config) -> Peripherals { critical_section::with(|cs| { let peripherals = Peripherals::take_with_cs(cs); @@ -112,9 +197,33 @@ pub fn init(_config: Config) -> Peripherals { crate::interrupt::typelevel::GPIOA::enable(); } + // SAFETY: Peripherals::take_with_cs will only be run once or panic. + unsafe { dma::init(cs, config.dma_burst_size, config.dma_round_robin) }; + #[cfg(feature = "_time-driver")] time_driver::init(cs); peripherals }) } + +pub(crate) mod sealed { + #[allow(dead_code)] + pub trait Sealed {} +} + +struct BitIter(u32); + +impl Iterator for BitIter { + type Item = u32; + + fn next(&mut self) -> Option { + match self.0.trailing_zeros() { + 32 => None, + b => { + self.0 &= !(1 << b); + Some(b) + } + } + } +} diff --git a/examples/mspm0g3507/.cargo/config.toml b/examples/mspm0g3507/.cargo/config.toml index 34c720cdd..e711afaf2 100644 --- a/examples/mspm0g3507/.cargo/config.toml +++ b/examples/mspm0g3507/.cargo/config.toml @@ -6,4 +6,4 @@ runner = "probe-rs run --chip MSPM0G3507 --protocol=swd" target = "thumbv6m-none-eabi" [env] -DEFMT_LOG = "debug" +DEFMT_LOG = "trace" diff --git a/tests/mspm0/Cargo.toml b/tests/mspm0/Cargo.toml index 386536bee..1c6f7d1cd 100644 --- a/tests/mspm0/Cargo.toml +++ b/tests/mspm0/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" [features] mspm0g3507 = [ "embassy-mspm0/mspm0g3507pm" ] +mspm0g3519 = [ "embassy-mspm0/mspm0g3519pz" ] [dependencies] teleprobe-meta = "1.1" diff --git a/tests/mspm0/build.rs b/tests/mspm0/build.rs index 0b58fb9e9..43a9ac04f 100644 --- a/tests/mspm0/build.rs +++ b/tests/mspm0/build.rs @@ -8,6 +8,9 @@ fn main() -> Result<(), Box> { #[cfg(feature = "mspm0g3507")] let memory_x = include_bytes!("memory_g3507.x"); + #[cfg(feature = "mspm0g3519")] + let memory_x = include_bytes!("memory_g3519.x"); + fs::write(out.join("memory.x"), memory_x).unwrap(); println!("cargo:rustc-link-search={}", out.display()); diff --git a/tests/mspm0/memory_g3519.x b/tests/mspm0/memory_g3519.x new file mode 100644 index 000000000..d62f10360 --- /dev/null +++ b/tests/mspm0/memory_g3519.x @@ -0,0 +1,6 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 128K + /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */ + RAM : ORIGIN = 0x20200000, LENGTH = 64K +} diff --git a/tests/mspm0/src/bin/dma.rs b/tests/mspm0/src/bin/dma.rs new file mode 100644 index 000000000..6fd973a18 --- /dev/null +++ b/tests/mspm0/src/bin/dma.rs @@ -0,0 +1,503 @@ +#![no_std] +#![no_main] + +#[cfg(feature = "mspm0g3507")] +teleprobe_meta::target!(b"lp-mspm0g3507"); + +#[cfg(feature = "mspm0g3519")] +teleprobe_meta::target!(b"lp-mspm0g3519"); + +use core::slice; + +use defmt::{assert, assert_eq, *}; +use embassy_executor::Spawner; +use embassy_mspm0::dma::{Channel, Transfer, TransferMode, TransferOptions, Word}; +use embassy_mspm0::Peri; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_mspm0::init(Default::default()); + info!("Hello World!"); + + { + info!("Single u8 read (blocking)"); + single_read(p.DMA_CH0.reborrow(), 0x41_u8); + + info!("Single u16 read (blocking)"); + single_read(p.DMA_CH0.reborrow(), 0xFF41_u16); + + info!("Single u32 read (blocking)"); + single_read(p.DMA_CH0.reborrow(), 0xFFEE_FF41_u32); + + info!("Single u64 read (blocking)"); + single_read(p.DMA_CH0.reborrow(), 0x0011_2233_FFEE_FF41_u64); + } + + // Widening transfers + { + info!("Single u8 read to u16"); + widening_single_read::(p.DMA_CH0.reborrow(), 0x41); + + info!("Single u8 read to u32"); + widening_single_read::(p.DMA_CH0.reborrow(), 0x43); + + info!("Single u8 read to u64"); + widening_single_read::(p.DMA_CH0.reborrow(), 0x47); + + info!("Single u16 read to u32"); + widening_single_read::(p.DMA_CH0.reborrow(), 0xAE43); + + info!("Single u16 read to u64"); + widening_single_read::(p.DMA_CH0.reborrow(), 0xAF47); + + info!("Single u32 read to u64"); + widening_single_read::(p.DMA_CH0.reborrow(), 0xDEAD_AF47); + } + + // Narrowing transfers. + { + info!("Single u16 read to u8"); + narrowing_single_read::(p.DMA_CH0.reborrow(), 0x4142); + + info!("Single u32 read to u8"); + narrowing_single_read::(p.DMA_CH0.reborrow(), 0x4142_2414); + + info!("Single u64 read to u8"); + narrowing_single_read::(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776); + + info!("Single u32 read to u16"); + narrowing_single_read::(p.DMA_CH0.reborrow(), 0x4142_2414); + + info!("Single u64 read to u16"); + narrowing_single_read::(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776); + + info!("Single u64 read to u32"); + narrowing_single_read::(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776); + } + + { + info!("Single u8 read (async)"); + async_single_read(p.DMA_CH0.reborrow(), 0x42_u8).await; + + info!("Single u16 read (async)"); + async_single_read(p.DMA_CH0.reborrow(), 0xAE42_u16).await; + + info!("Single u32 read (async)"); + async_single_read(p.DMA_CH0.reborrow(), 0xFE44_1500_u32).await; + + info!("Single u64 read (async)"); + async_single_read(p.DMA_CH0.reborrow(), 0x8F7F_6F5F_4F3F_2F1F_u64).await; + } + + { + info!("Multiple u8 reads (blocking)"); + block_read::<_, 16>(p.DMA_CH0.reborrow(), 0x98_u8); + + info!("Multiple u16 reads (blocking)"); + block_read::<_, 2>(p.DMA_CH0.reborrow(), 0x9801_u16); + + info!("Multiple u32 reads (blocking)"); + block_read::<_, 4>(p.DMA_CH0.reborrow(), 0x9821_9801_u32); + + info!("Multiple u64 reads (blocking)"); + block_read::<_, 4>(p.DMA_CH0.reborrow(), 0xABCD_EF01_2345_6789_u64); + } + + { + info!("Multiple u8 reads (async)"); + async_block_read::<_, 8>(p.DMA_CH0.reborrow(), 0x86_u8).await; + + info!("Multiple u16 reads (async)"); + async_block_read::<_, 6>(p.DMA_CH0.reborrow(), 0x7777_u16).await; + + info!("Multiple u32 reads (async)"); + async_block_read::<_, 3>(p.DMA_CH0.reborrow(), 0xA5A5_A5A5_u32).await; + + info!("Multiple u64 reads (async)"); + async_block_read::<_, 14>(p.DMA_CH0.reborrow(), 0x5A5A_5A5A_A5A5_A5A5_u64).await; + } + + // Intentionally skip testing multiple reads in single transfer mode. + // + // If the destination length is greater than 1 and single transfer mode is used then two transfers + // are performed in a trigger. Similarly with any other length of destination above 2, only 2 transfers + // are performed. Issuing another trigger (resume) results in no further progress. More than likely + // the test does not work due to some combination of a hardware bug and the datasheet being unclear + // regarding what ends a software trigger. + // + // However this case works fine with a hardware trigger (such as the ADC hardware trigger). + + { + info!("Single u8 write (blocking)"); + single_write(p.DMA_CH0.reborrow(), 0x41_u8); + + info!("Single u16 write (blocking)"); + single_write(p.DMA_CH0.reborrow(), 0x4142_u16); + + info!("Single u32 write (blocking)"); + single_write(p.DMA_CH0.reborrow(), 0x4142_4344_u32); + + info!("Single u64 write (blocking)"); + single_write(p.DMA_CH0.reborrow(), 0x4142_4344_4546_4748_u64); + } + + { + info!("Single u8 write (async)"); + async_single_write(p.DMA_CH0.reborrow(), 0xAA_u8).await; + + info!("Single u16 write (async)"); + async_single_write(p.DMA_CH0.reborrow(), 0xBBBB_u16).await; + + info!("Single u32 write (async)"); + async_single_write(p.DMA_CH0.reborrow(), 0xCCCC_CCCC_u32).await; + + info!("Single u64 write (async)"); + async_single_write(p.DMA_CH0.reborrow(), 0xDDDD_DDDD_DDDD_DDDD_u64).await; + } + + { + info!("Multiple u8 writes (blocking)"); + block_write(p.DMA_CH0.reborrow(), &[0xFF_u8, 0x7F, 0x3F, 0x1F]); + + info!("Multiple u16 writes (blocking)"); + block_write(p.DMA_CH0.reborrow(), &[0xFFFF_u16, 0xFF7F, 0xFF3F, 0xFF1F]); + + info!("Multiple u32 writes (blocking)"); + block_write( + p.DMA_CH0.reborrow(), + &[0xFF00_00FF_u32, 0xFF00_007F, 0x0000_FF3F, 0xFF1F_0000], + ); + + info!("Multiple u64 writes (blocking)"); + block_write( + p.DMA_CH0.reborrow(), + &[ + 0xFF00_0000_0000_00FF_u64, + 0x0000_FF00_007F_0000, + 0x0000_FF3F_0000_0000, + 0xFF1F_0000_1111_837A, + ], + ); + } + + { + info!("Multiple u8 writes (async)"); + async_block_write(p.DMA_CH0.reborrow(), &[0u8, 1, 2, 3]).await; + + info!("Multiple u16 writes (async)"); + async_block_write(p.DMA_CH0.reborrow(), &[0x9801u16, 0x9802, 0x9803, 0x9800, 0x9000]).await; + + info!("Multiple u32 writes (async)"); + async_block_write(p.DMA_CH0.reborrow(), &[0x9801_ABCDu32, 0xFFAC_9802, 0xDEAD_9803]).await; + + info!("Multiple u64 writes (async)"); + async_block_write( + p.DMA_CH0.reborrow(), + &[ + 0xA55A_1111_3333_5555_u64, + 0x1111_A55A_3333_5555, + 0x5555_A55A_3333_1111, + 0x01234_5678_89AB_CDEF, + ], + ) + .await; + } + + // TODO: Mixed byte and word transfers. + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn single_read(mut channel: Peri<'_, impl Channel>, mut src: W) { + let options = TransferOptions::default(); + let mut dst = W::default(); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_read( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &mut src, + slice::from_mut(&mut dst), + options, + )) + }; + transfer.blocking_wait(); + + assert_eq!(src, dst); +} + +async fn async_single_read( + mut channel: Peri<'_, impl Channel>, + mut src: W, +) { + let options = TransferOptions::default(); + let mut dst = W::default(); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_read( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &mut src, + slice::from_mut(&mut dst), + options, + )) + }; + transfer.await; + + assert_eq!(src, dst); +} + +fn block_read( + mut channel: Peri<'_, impl Channel>, + mut src: W, +) { + let mut options = TransferOptions::default(); + // Complete the entire transfer. + options.mode = TransferMode::Block; + + let mut dst = [W::default(); N]; + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_read( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &mut src, + &mut dst[..], + options, + )) + }; + transfer.blocking_wait(); + + assert_eq!(dst, [src; N]); +} + +async fn async_block_read( + mut channel: Peri<'_, impl Channel>, + mut src: W, +) { + let mut options = TransferOptions::default(); + // Complete the entire transfer. + options.mode = TransferMode::Block; + + let mut dst = [W::default(); N]; + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_read( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &mut src, + &mut dst[..], + options, + )) + }; + transfer.await; + + assert_eq!(dst, [src; N]); +} + +fn single_write(mut channel: Peri<'_, impl Channel>, src: W) { + let options = TransferOptions::default(); + let mut dst = W::default(); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_write( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + slice::from_ref(&src), + &mut dst, + options, + )) + }; + transfer.blocking_wait(); + + assert_eq!(src, dst); +} + +async fn async_single_write(mut channel: Peri<'_, impl Channel>, src: W) { + let options = TransferOptions::default(); + let mut dst = W::default(); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_write( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + slice::from_ref(&src), + &mut dst, + options, + )) + }; + transfer.await; + + assert_eq!(src, dst); +} + +fn block_write(mut channel: Peri<'_, impl Channel>, src: &[W]) { + let mut options = TransferOptions::default(); + // Complete the entire transfer. + options.mode = TransferMode::Block; + + let mut dst = W::default(); + + // Starting from 1 because a zero length transfer does nothing. + for i in 1..src.len() { + info!("-> {} write(s)", i); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_write( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &src[..i], + &mut dst, + options, + )) + }; + transfer.blocking_wait(); + + // The result will be the last value written. + assert_eq!(dst, src[i - 1]); + } +} + +async fn async_block_write(mut channel: Peri<'_, impl Channel>, src: &[W]) { + let mut options = TransferOptions::default(); + // Complete the entire transfer. + options.mode = TransferMode::Block; + + let mut dst = W::default(); + + // Starting from 1 because a zero length transfer does nothing. + for i in 1..src.len() { + info!("-> {} write(s)", i); + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_write( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &src[..i], + &mut dst, + options, + )) + }; + transfer.await; + + // The result will be the last value written. + assert_eq!(dst, src[i - 1]); + } +} + +/// [`single_read`], but testing when the destination is wider than the source. +/// +/// The MSPM0 DMA states that the upper bytes when the destination is longer than the source are zeroed. +/// This matches the behavior in Rust for all unsigned integer types. +fn widening_single_read(mut channel: Peri<'_, impl Channel>, mut src: SW) +where + SW: Word + Copy + Default + Eq + defmt::Format, + DW: Word + Copy + Default + Eq + defmt::Format + From, +{ + assert!( + DW::size() > SW::size(), + "This test only works when the destination is larger than the source" + ); + + let options = TransferOptions::default(); + let mut dst = DW::default(); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_read( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &mut src, + slice::from_mut(&mut dst), + options, + )) + }; + transfer.blocking_wait(); + + assert_eq!(DW::from(src), dst); +} + +/// [`single_read`], but testing when the destination is narrower than the source. +/// +/// The MSPM0 DMA states that the upper bytes when the source is longer than the destination are dropped. +/// This matches the behavior in Rust for all unsigned integer types. +fn narrowing_single_read(mut channel: Peri<'_, impl Channel>, mut src: SW) +where + SW: Word + Copy + Default + Eq + defmt::Format + From, + DW: Word + Copy + Default + Eq + defmt::Format + Narrow, +{ + assert!( + SW::size() > DW::size(), + "This test only works when the source is larger than the destination" + ); + + let options = TransferOptions::default(); + let mut dst = DW::default(); + + // SAFETY: src and dst outlive the transfer. + let transfer = unsafe { + unwrap!(Transfer::new_read( + channel.reborrow(), + Transfer::SOFTWARE_TRIGGER, + &mut src, + slice::from_mut(&mut dst), + options, + )) + }; + transfer.blocking_wait(); + + // The expected value is the source value masked by the maximum destination value. + // This is effectively `src as DW as SW` to drop the upper byte(s). + let expect = SW::from(DW::narrow(src)); + assert_eq!(expect, dst.into()); +} + +/// A pseudo `as` trait to allow downcasting integer types (TryFrom could fail). +trait Narrow { + fn narrow(value: T) -> Self; +} + +impl Narrow for u8 { + fn narrow(value: u16) -> Self { + value as u8 + } +} + +impl Narrow for u8 { + fn narrow(value: u32) -> Self { + value as u8 + } +} + +impl Narrow for u8 { + fn narrow(value: u64) -> Self { + value as u8 + } +} + +impl Narrow for u16 { + fn narrow(value: u32) -> Self { + value as u16 + } +} + +impl Narrow for u16 { + fn narrow(value: u64) -> Self { + value as u16 + } +} + +impl Narrow for u32 { + fn narrow(value: u64) -> Self { + value as u32 + } +} diff --git a/tests/mspm0/src/bin/uart.rs b/tests/mspm0/src/bin/uart.rs index 458129d44..916ce0d4b 100644 --- a/tests/mspm0/src/bin/uart.rs +++ b/tests/mspm0/src/bin/uart.rs @@ -4,6 +4,9 @@ #[cfg(feature = "mspm0g3507")] teleprobe_meta::target!(b"lp-mspm0g3507"); +#[cfg(feature = "mspm0g3519")] +teleprobe_meta::target!(b"lp-mspm0g3519"); + use defmt::{assert_eq, unwrap, *}; use embassy_executor::Spawner; use embassy_mspm0::mode::Blocking; @@ -23,7 +26,7 @@ async fn main(_spawner: Spawner) { // TODO: Allow creating a looped-back UART (so pins are not needed). // Do not select default UART since the virtual COM port is attached to UART0. - #[cfg(feature = "mspm0g3507")] + #[cfg(any(feature = "mspm0g3507", feature = "mspm0g3519"))] let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1); const MFCLK_BUAD_RATES: &[u32] = &[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]; -- cgit From 504261a8d0bc58fcfa8b73245eaf859f88d62a94 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 7 Jul 2025 23:33:10 +0200 Subject: Reenable rpi pico tests. --- ci.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ci.sh b/ci.sh index e54112359..dddef11ae 100755 --- a/ci.sh +++ b/ci.sh @@ -372,9 +372,6 @@ rm out/tests/pimoroni-pico-plus-2/adc # temporarily disabled rm out/tests/pimoroni-pico-plus-2/pwm -# temporarily disabled, bad hardware connection. -rm -f out/tests/rpi-pico/* - if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit -- cgit From 2fe2a0cf9c15bf7e84134cd7fec48017bb0a0db7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 8 Jul 2025 20:19:01 +0200 Subject: excutor: fix Send unsoundness with `-> impl Future` tasks. --- embassy-executor-macros/src/macros/task.rs | 10 ++++-- embassy-executor/tests/ui.rs | 2 ++ .../tests/ui/return_impl_future_nonsend.rs | 21 +++++++++++ .../tests/ui/return_impl_future_nonsend.stderr | 17 +++++++++ embassy-executor/tests/ui/return_impl_send.stderr | 8 ++--- embassy-executor/tests/ui/spawn_nonsend.rs | 16 +++++++++ embassy-executor/tests/ui/spawn_nonsend.stderr | 41 ++++++++++++++++++++++ 7 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 embassy-executor/tests/ui/return_impl_future_nonsend.rs create mode 100644 embassy-executor/tests/ui/return_impl_future_nonsend.stderr create mode 100644 embassy-executor/tests/ui/spawn_nonsend.rs create mode 100644 embassy-executor/tests/ui/spawn_nonsend.stderr diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 91bf8e940..1c5e3571d 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -131,6 +131,12 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { )); } + let spawn = if returns_impl_trait { + quote!(spawn) + } else { + quote!(_spawn_async_fn) + }; + #[cfg(feature = "nightly")] let mut task_outer_body = quote! { trait _EmbassyInternalTaskTrait { @@ -147,7 +153,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { const POOL_SIZE: usize = #pool_size; static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new(); - unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } + unsafe { POOL.#spawn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } }; #[cfg(not(feature = "nightly"))] let mut task_outer_body = quote! { @@ -164,7 +170,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)}, {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)}, > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) }; - unsafe { __task_pool_get(#task_inner_ident)._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } + unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) } }; let task_outer_attrs = task_inner.attrs.clone(); diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs index c4a1a601c..7757775ee 100644 --- a/embassy-executor/tests/ui.rs +++ b/embassy-executor/tests/ui.rs @@ -17,6 +17,8 @@ fn ui() { t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); t.compile_fail("tests/ui/not_async.rs"); + t.compile_fail("tests/ui/spawn_nonsend.rs"); + t.compile_fail("tests/ui/return_impl_future_nonsend.rs"); if rustversion::cfg!(stable) { // output is slightly different on nightly t.compile_fail("tests/ui/bad_return_impl_future.rs"); diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.rs b/embassy-executor/tests/ui/return_impl_future_nonsend.rs new file mode 100644 index 000000000..b8c184b21 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_future_nonsend.rs @@ -0,0 +1,21 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +use core::future::Future; + +use embassy_executor::SendSpawner; + +#[embassy_executor::task] +fn task() -> impl Future { + // runs in spawning thread + let non_send: *mut () = core::ptr::null_mut(); + async move { + // runs in executor thread + println!("{}", non_send as usize); + } +} + +fn send_spawn(s: SendSpawner) { + s.spawn(task()).unwrap(); +} + +fn main() {} diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.stderr b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr new file mode 100644 index 000000000..8aeb9738a --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr @@ -0,0 +1,17 @@ +error: future cannot be sent between threads safely + --> tests/ui/return_impl_future_nonsend.rs:18:13 + | +18 | s.spawn(task()).unwrap(); + | ^^^^^^ future created by async block is not `Send` + | + = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()` +note: captured value is not `Send` + --> tests/ui/return_impl_future_nonsend.rs:13:24 + | +13 | println!("{}", non_send as usize); + | ^^^^^^^^ has type `*mut ()` which is not `Send` +note: required by a bound in `SendSpawner::spawn` + --> src/spawner.rs + | + | pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + | ^^^^ required by this bound in `SendSpawner::spawn` diff --git a/embassy-executor/tests/ui/return_impl_send.stderr b/embassy-executor/tests/ui/return_impl_send.stderr index cd693af2b..759be1cde 100644 --- a/embassy-executor/tests/ui/return_impl_send.stderr +++ b/embassy-executor/tests/ui/return_impl_send.stderr @@ -91,14 +91,14 @@ error[E0277]: `impl Send` is not a future | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future | = help: the trait `Future` is not implemented for `impl Send` -note: required by a bound in `TaskPool::::_spawn_async_fn` +note: required by a bound in `TaskPool::::spawn` --> src/raw/mod.rs | | impl TaskPool { - | ^^^^^^ required by this bound in `TaskPool::::_spawn_async_fn` + | ^^^^^^ required by this bound in `TaskPool::::spawn` ... - | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - | --------------- required by a bound in this associated function + | pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + | ----- required by a bound in this associated function = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: task futures must resolve to `()` or `!` diff --git a/embassy-executor/tests/ui/spawn_nonsend.rs b/embassy-executor/tests/ui/spawn_nonsend.rs new file mode 100644 index 000000000..4c4cc7697 --- /dev/null +++ b/embassy-executor/tests/ui/spawn_nonsend.rs @@ -0,0 +1,16 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +use core::future::Future; + +use embassy_executor::SendSpawner; + +#[embassy_executor::task] +async fn task(non_send: *mut ()) { + println!("{}", non_send as usize); +} + +fn send_spawn(s: SendSpawner) { + s.spawn(task(core::ptr::null_mut())).unwrap(); +} + +fn main() {} diff --git a/embassy-executor/tests/ui/spawn_nonsend.stderr b/embassy-executor/tests/ui/spawn_nonsend.stderr new file mode 100644 index 000000000..2a06c8b94 --- /dev/null +++ b/embassy-executor/tests/ui/spawn_nonsend.stderr @@ -0,0 +1,41 @@ +warning: unused import: `core::future::Future` + --> tests/ui/spawn_nonsend.rs:3:5 + | +3 | use core::future::Future; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_imports)]` on by default + +error[E0277]: `*mut ()` cannot be sent between threads safely + --> tests/ui/spawn_nonsend.rs:13:13 + | +7 | #[embassy_executor::task] + | ------------------------- within this `impl Sized` +... +13 | s.spawn(task(core::ptr::null_mut())).unwrap(); + | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely + | | + | required by a bound introduced by this call + | + = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()` +note: required because it's used within this closure + --> tests/ui/spawn_nonsend.rs:7:1 + | +7 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `impl Sized` + --> src/raw/mod.rs + | + | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + | ^^^^^^^^^^ +note: required because it appears within the type `impl Sized` + --> tests/ui/spawn_nonsend.rs:7:1 + | +7 | #[embassy_executor::task] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `SendSpawner::spawn` + --> src/spawner.rs + | + | pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + | ^^^^ required by this bound in `SendSpawner::spawn` + = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) -- cgit From 0c136c7b050ded4bf660ea7a50381698ab9d5f09 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 8 Jul 2025 22:39:53 +0200 Subject: executor: mark Spawner::for_current_executor() as unsafe. It's unsound with manually-created Contexts, see https://github.com/embassy-rs/embassy/issues/4379 --- embassy-executor/src/spawner.rs | 18 +++++++++++++++++- .../nrf52840/src/bin/self_spawn_current_executor.rs | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 522d97db3..2909d19a0 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -122,10 +122,26 @@ impl Spawner { /// This function is `async` just to get access to the current async /// context. It returns instantly, it does not block/yield. /// + /// Using this method is discouraged due to it being unsafe. Consider the following + /// alternatives instead: + /// + /// - Pass the initial `Spawner` as an argument to tasks. Note that it's `Copy`, so you can + /// make as many copies of it as you want. + /// - Use `SendSpawner::for_current_executor()` instead, which is safe but can only be used + /// if task arguments are `Send`. + /// + /// The only case where using this method is absolutely required is obtaining the `Spawner` + /// for an `InterruptExecutor`. + /// + /// # Safety + /// + /// You must only execute this with an async `Context` created by the Embassy executor. + /// You must not execute it with manually-created `Context`s. + /// /// # Panics /// /// Panics if the current executor is not an Embassy executor. - pub fn for_current_executor() -> impl Future { + pub unsafe fn for_current_executor() -> impl Future { poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); let executor = unsafe { diff --git a/examples/nrf52840/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs index ec9569a64..ddb40dc53 100644 --- a/examples/nrf52840/src/bin/self_spawn_current_executor.rs +++ b/examples/nrf52840/src/bin/self_spawn_current_executor.rs @@ -10,7 +10,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn my_task(n: u32) { Timer::after_secs(1).await; info!("Spawning self! {}", n); - unwrap!(Spawner::for_current_executor().await.spawn(my_task(n + 1))); + let spawner = unsafe { Spawner::for_current_executor().await }; + unwrap!(spawner.spawn(my_task(n + 1))); } #[embassy_executor::main] -- cgit From 0448d1fc50e80832ecd6382ad3add9575d505d37 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:42:45 +0200 Subject: fix `release/bump-dependency.sh`: don't generate backup files the `-rie` arguments of `sed` were probably meant as `-r -i -e`, however, due to the way it was written it ended up being `-r -i e`, thus causing `-i` (edit in-place) to generate backup files. so for every edited `Cargo.toml` it also created a `Cargo.tomle` with the previous content. `-e` is anyway not needed as the last argument of `sed` here is the expression to be executed. --- release/bump-dependency.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/bump-dependency.sh b/release/bump-dependency.sh index 07511d229..97f73cddc 100755 --- a/release/bump-dependency.sh +++ b/release/bump-dependency.sh @@ -8,4 +8,4 @@ # CRATE=$1 TARGET_VER=$2 -find . -name "Cargo.toml" | xargs sed -rie "s/($CRATE = \{.*version = \")[0-9]+.[0-9]+.?[0-9]*(\".*)/\1$TARGET_VER\2/g" +find . -name "Cargo.toml" | xargs sed -ri "s/($CRATE = \{.*version = \")[0-9]+.[0-9]+.?[0-9]*(\".*)/\1$TARGET_VER\2/g" -- cgit From ca21140f5ee326e1a0723fb56a39e73a98e3188a Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:00:46 +0200 Subject: add changelog for `embassy-futures` --- embassy-futures/CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 embassy-futures/CHANGELOG.md diff --git a/embassy-futures/CHANGELOG.md b/embassy-futures/CHANGELOG.md new file mode 100644 index 000000000..86ea40540 --- /dev/null +++ b/embassy-futures/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog for embassy-futures + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Preserve location information for `defmt` in `fmt` calls ([#3085](https://github.com/embassy-rs/embassy/pull/3085)) +- Fixed soundness issue in `select_slice` ([#3328](https://github.com/embassy-rs/embassy/pull/3328)) +- Added `select5` and `select6` ([#3430](https://github.com/embassy-rs/embassy/pull/3430)) +- Added `is_x` methods for all `EitherN` enum variants (#[3650](https://github.com/embassy-rs/embassy/pull/3650)) -- cgit From 1bc17b0964a12f2c31275710c90e446f842c027a Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:24:04 +0200 Subject: `embassy-futures`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-futures/CHANGELOG.md | 3 ++- embassy-futures/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-futures/release.toml diff --git a/embassy-futures/CHANGELOG.md b/embassy-futures/CHANGELOG.md index 86ea40540..eb76cdc4a 100644 --- a/embassy-futures/CHANGELOG.md +++ b/embassy-futures/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Preserve location information for `defmt` in `fmt` calls ([#3085](https://github.com/embassy-rs/embassy/pull/3085)) - Fixed soundness issue in `select_slice` ([#3328](https://github.com/embassy-rs/embassy/pull/3328)) diff --git a/embassy-futures/release.toml b/embassy-futures/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-futures/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 88076255baf1c8e5b25d0fb32dfa3f907030e2c2 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:49:57 +0200 Subject: add changelog for `embassy-usb-driver` --- embassy-usb-driver/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 embassy-usb-driver/CHANGELOG.md diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md new file mode 100644 index 000000000..4a1090029 --- /dev/null +++ b/embassy-usb-driver/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog for embassy-usb-driver + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) -- cgit From 1d522c6e4fedc045e6ed7483ffc2f91a99e2d9a6 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:51:37 +0200 Subject: `embassy-usb-driver`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-usb-driver/CHANGELOG.md | 3 ++- embassy-usb-driver/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-usb-driver/release.toml diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md index 4a1090029..c02daefdf 100644 --- a/embassy-usb-driver/CHANGELOG.md +++ b/embassy-usb-driver/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) diff --git a/embassy-usb-driver/release.toml b/embassy-usb-driver/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-usb-driver/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From e9296b547a6bc6f6d69f28882fbd95107f0d5e98 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:57:03 +0200 Subject: prepare changelog for `embassy-net-driver-channel` v0.4.0 --- embassy-net-driver-channel/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-net-driver-channel/CHANGELOG.md b/embassy-net-driver-channel/CHANGELOG.md index d7af7e55d..ebc0e96b9 100644 --- a/embassy-net-driver-channel/CHANGELOG.md +++ b/embassy-net-driver-channel/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Update `embassy-sync` to v0.7.0 + ## 0.3.0 - 2024-08-05 - Add collapse_debuginfo to fmt.rs macros. -- cgit From 010c626a1160cf1a6721d8632119c0e45907e8c2 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 10:58:39 +0200 Subject: `embassy-inet-driver-channel`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-net-driver-channel/CHANGELOG.md | 3 ++- embassy-net-driver-channel/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-net-driver-channel/release.toml diff --git a/embassy-net-driver-channel/CHANGELOG.md b/embassy-net-driver-channel/CHANGELOG.md index ebc0e96b9..a5c81cf4d 100644 --- a/embassy-net-driver-channel/CHANGELOG.md +++ b/embassy-net-driver-channel/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Update `embassy-sync` to v0.7.0 diff --git a/embassy-net-driver-channel/release.toml b/embassy-net-driver-channel/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-net-driver-channel/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From e38b3c52528da3796208ac9a05e4a75e373cf252 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 11:09:09 +0200 Subject: prepare changelog for `embassy-usb` v0.5.0 --- embassy-usb/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 76fafed31..d941ae068 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- `UAC1`: unmute by default ([#3992](https://github.com/embassy-rs/embassy/pull/3992)) +- `cdc_acm`: `State::new` is now `const` ([#4000](https://github.com/embassy-rs/embassy/pull/4000)) +- Add support for CMSIS-DAP v2 USB class ([#4107](https://github.com/embassy-rs/embassy/pull/4107)) +- Reduce `UsbDevice` builder logs to `trace` ([#4130](https://github.com/embassy-rs/embassy/pull/4130)) +- Implement `embedded-io-async` traits for USB CDC ACM ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) +- Update `embassy-sync` to v0.7.0 + ## 0.4.0 - 2025-01-15 - Change config defaults to to composite with IADs. This ensures embassy-usb Just Works in more cases when using classes with multiple interfaces, or multiple classes. (breaking change) -- cgit From 27bae26172757b15597f497b5e3d30d5dc18bd98 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 11:10:21 +0200 Subject: `embassy-usb`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-usb/CHANGELOG.md | 3 ++- embassy-usb/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-usb/release.toml diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index d941ae068..51db5f03e 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - `UAC1`: unmute by default ([#3992](https://github.com/embassy-rs/embassy/pull/3992)) - `cdc_acm`: `State::new` is now `const` ([#4000](https://github.com/embassy-rs/embassy/pull/4000)) diff --git a/embassy-usb/release.toml b/embassy-usb/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-usb/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 1c515937ff69cf2358118e13a9a70178dd7a99a0 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Wed, 11 Jun 2025 09:47:25 +0200 Subject: prepare changelog for `embassy-executor` v0.8.0 --- embassy-executor/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 608c67724..19e41e3e7 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,7 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased +- Added `SpawnToken::id` +- Task pools are now statically allocated on stable rust. All `task-arena-size-*` features have been removed and are no longer necessary. +- New trace hooks: `_embassy_trace_poll_start` & `_embassy_trace_task_end` +- Added task naming capability to tracing infrastructure +- Added `Executor::id` & `Spawner::executor_id` +- Disable `critical-section/std` for arch-std +- Added possibility to select an executor in `#[embassy_executor::main]` +- Fix AVR executor +- executor: Make state implementations and their conditions match - Added support for Cortex-A and Cortex-R +- Added support for `-> impl Future` in `#[task]` +- Fixed `Send` unsoundness with `-> impl Future` tasks +- Marked `Spawner::for_current_executor` as `unsafe` ## 0.7.0 - 2025-01-02 -- cgit From a52ca758ac84af5b9fafa9f6cc0562495a796ffd Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Wed, 11 Jun 2025 09:48:37 +0200 Subject: `embassy-executor`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-executor/CHANGELOG.md | 3 ++- embassy-executor/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-executor/release.toml diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 19e41e3e7..914863a83 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## unreleased + +## Unreleased - ReleaseDate - Added `SpawnToken::id` - Task pools are now statically allocated on stable rust. All `task-arena-size-*` features have been removed and are no longer necessary. diff --git a/embassy-executor/release.toml b/embassy-executor/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-executor/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 9f9c6f328d1da8a4ec7bea41b4d1355665af55cb Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:14:56 +0200 Subject: prepare changelog for `embassy-time-driver` v0.2.1 --- embassy-time-driver/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index 744b0f648..349df15a7 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Allow inlining on time driver boundary +- add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` + ## 0.2.0 - 2025-01-02 - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. -- cgit From 80bb09cdb1599e185023ae39d144f353f6ebc10f Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:16:07 +0200 Subject: `embassy-time-driver`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-time-driver/CHANGELOG.md | 3 ++- embassy-time-driver/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-time-driver/release.toml diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index 349df15a7..b61a10bf6 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Allow inlining on time driver boundary - add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` diff --git a/embassy-time-driver/release.toml b/embassy-time-driver/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-time-driver/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From bb1b682deb5931b24ea7bd191aadf5a927140b14 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:23:11 +0200 Subject: prepare changelog for `embassy-time` v0.4.1 --- embassy-time/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 09e951ce4..49b3b4586 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Allow inlining on time driver boundary +- Add `saturating_add` and `saturating_sub` to `Instant` +- Add `Instant::try_from_*` constructor functions +- Add `Duration::try_from_*` constructor functions +- Don't select `critical-section` impl for `std` +- Manually implement the future for `with_timeout` +- Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` + ## 0.4.0 - 2025-01-02 - `embassy-time-driver` updated from v0.1 to v0.2. -- cgit From 112c7a16654bf376463586bd2a32ea817fb7f11d Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:32:28 +0200 Subject: `embassy-time`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-time/CHANGELOG.md | 3 ++- embassy-time/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-time/release.toml diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 49b3b4586..a0c1abe8d 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Allow inlining on time driver boundary - Add `saturating_add` and `saturating_sub` to `Instant` diff --git a/embassy-time/release.toml b/embassy-time/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-time/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From db58ce03cc4bde8582c12c5736a8a4490e160a85 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:26:51 +0200 Subject: prepare changelog for `embassy-embedded-hal` v0.4.0 --- embassy-embedded-hal/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-embedded-hal/CHANGELOG.md b/embassy-embedded-hal/CHANGELOG.md index 224036af4..d19c54354 100644 --- a/embassy-embedded-hal/CHANGELOG.md +++ b/embassy-embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- `SpiDevice` cancel safety: always set CS pin to high on drop +- Update `embassy-sync` to v0.7.0 + ## 0.3.0 - 2025-01-05 - The `std` feature has been removed -- cgit From cd2c9c7e85efb055b3c72971f0a94cc75efd0aa1 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:30:26 +0200 Subject: `embassy-embedded-hal`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-embedded-hal/CHANGELOG.md | 3 ++- embassy-embedded-hal/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-embedded-hal/release.toml diff --git a/embassy-embedded-hal/CHANGELOG.md b/embassy-embedded-hal/CHANGELOG.md index d19c54354..6f0655adf 100644 --- a/embassy-embedded-hal/CHANGELOG.md +++ b/embassy-embedded-hal/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - `SpiDevice` cancel safety: always set CS pin to high on drop - Update `embassy-sync` to v0.7.0 diff --git a/embassy-embedded-hal/release.toml b/embassy-embedded-hal/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-embedded-hal/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 74101a8625ad042d3bfd44f9dc20882a8d1f6944 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:03:59 +0200 Subject: prepare changelog for `embassy-rp` v0.5.0 --- embassy-rp/CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 7ac0a47cb..bb58ac54f 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975)) +- Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012)) +- `flash`: rename `BOOTROM_BASE` to `BOOTRAM_BASE` ([#4014](https://github.com/embassy-rs/embassy/pull/4014)) +- Remove `Peripheral` trait & rename `PeripheralRef` to `Peri` ([#3999](https://github.com/embassy-rs/embassy/pull/3999)) +- Fix watchdog count on RP235x ([#4021](https://github.com/embassy-rs/embassy/pull/4021)) +- I2C: ensure that wakers are registered before checking status of `wait_on` helpers ([#4043](https://github.com/embassy-rs/embassy/pull/4043)) +- Modify `Uarte` and `BufferedUarte` initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) +- `uart`: increase RX FIFO watermark from 1/8 to 7/8 ([#4055](https://github.com/embassy-rs/embassy/pull/4055)) +- Add `spinlock_mutex` ([#4017](https://github.com/embassy-rs/embassy/pull/4017)) +- Enable input mode for PWM pins on RP235x and disable it on drop ([#4093](https://github.com/embassy-rs/embassy/pull/4093)) +- Add `impl rand_core::CryptoRng for Trng` ([#4096](https://github.com/embassy-rs/embassy/pull/4096)) +- `pwm`: enable pull-down resistors for pins in `Drop` implementation ([#4115](https://github.com/embassy-rs/embassy/pull/4115)) +- Rewrite PIO onewire implementation ([#4128](https://github.com/embassy-rs/embassy/pull/4128)) +- Implement RP2040 overclocking ([#4150](https://github.com/embassy-rs/embassy/pull/4150)) +- Implement RP235x overclocking ([#4187](https://github.com/embassy-rs/embassy/pull/4187)) +- `trng`: improve error handling ([#4139](https://github.com/embassy-rs/embassy/pull/4139)) +- Remove `` from `Uart` and `BufferedUart` ([#4155](https://github.com/embassy-rs/embassy/pull/4155)) +- Make bit-depth of I2S PIO program configurable ([#4193](https://github.com/embassy-rs/embassy/pull/4193)) +- Add the possibility to document `bind_interrupts` `struct`s ([#4206](https://github.com/embassy-rs/embassy/pull/4206)) +- Add missing `Debug` and `defmt::Format` `derive`s for ADC & `AnyPin` ([#4205](https://github.com/embassy-rs/embassy/pull/4205)) +- Add `rand-core` v0.9 support ([#4217](https://github.com/embassy-rs/embassy/pull/4217)) +- Update `embassy-sync` to v0.7.0 ([#4234](https://github.com/embassy-rs/embassy/pull/4234)) +- Add compatibility with ws2812 leds that have 4 addressable lights ([#4236](https://github.com/embassy-rs/embassy/pull/4236)) +- Implement input/output inversion ([#4237](https://github.com/embassy-rs/embassy/pull/4237)) +- Add `multicore::current_core` API ([#4362](https://github.com/embassy-rs/embassy/pull/4362)) + ## 0.4.0 - 2025-03-09 - Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857)) -- cgit From 22e77cb675a2405472cd6d28b66172e311713ee1 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 8 Jun 2025 12:05:29 +0200 Subject: `embassy-rp`: add release automation using `cargo-release` this requires you to install [`cargo-release`]. note that this does not include a URL pointing to the diff on GitHub as is usually done in changelogs since `embassy` is a mono-repo and the GH UI doesn't offer a commit view per folder (see the [GH feature request] for this). [`cargo-release`]: https://crates.io/crates/cargo-release [GH feature request]: https://github.com/orgs/community/discussions/162131 --- embassy-rp/CHANGELOG.md | 3 ++- embassy-rp/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/release.toml diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index bb58ac54f..52bf0038e 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975)) - Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012)) diff --git a/embassy-rp/release.toml b/embassy-rp/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-rp/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 6609a85f3ce941ef07e471e56981a819b17e9303 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Wed, 25 Jun 2025 22:56:11 -0500 Subject: nxp: add feature for lpc55 this is needed since I will be working on adding support for the MCX families to embassy-nxp Co-authored-by: IriniaCh524 --- ci.sh | 2 +- embassy-nxp/Cargo.toml | 10 ++++++++-- examples/lpc55s69/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ci.sh b/ci.sh index e1fdf998a..036c38d1a 100755 --- a/ci.sh +++ b/ci.sh @@ -176,7 +176,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf \ + --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 426af06a0..010607914 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -9,12 +9,18 @@ cortex-m-rt = "0.7.0" critical-section = "1.1.2" embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -lpc55-pac = "0.5.0" defmt = { version = "1", optional = true } +## Chip dependencies +lpc55-pac = { version = "0.5.0", optional = true } + [features] default = ["rt"] -rt = ["lpc55-pac/rt"] +# Enable PACs as optional dependencies, since some chip families will use different pac crates. +rt = ["lpc55-pac?/rt"] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] + +#! ### Chip selection features +lpc55 = ["lpc55-pac"] diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 7f81e9c7f..6ec6e51a8 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] -embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } -- cgit From 7219df4a5f9a666c6b0114eb9e86b6ab088fdeaa Mon Sep 17 00:00:00 2001 From: i509VCB Date: Sun, 6 Jul 2025 22:39:44 -0500 Subject: nxp: feature gate lpc55 gpio and pint to lpc55 --- embassy-nxp/Cargo.toml | 6 + embassy-nxp/src/chips/lpc55.rs | 70 +++++ embassy-nxp/src/gpio.rs | 357 +--------------------- embassy-nxp/src/gpio/lpc55.rs | 677 +++++++++++++++++++++++++++++++++++++++++ embassy-nxp/src/lib.rs | 90 +----- embassy-nxp/src/pac_utils.rs | 323 -------------------- embassy-nxp/src/pint.rs | 3 +- 7 files changed, 774 insertions(+), 752 deletions(-) create mode 100644 embassy-nxp/src/chips/lpc55.rs create mode 100644 embassy-nxp/src/gpio/lpc55.rs delete mode 100644 embassy-nxp/src/pac_utils.rs diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 010607914..18e9d989c 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -22,5 +22,11 @@ rt = ["lpc55-pac?/rt"] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] +## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable) +unstable-pac = [] +# This is unstable because semver-minor (non-breaking) releases of embassy-nrf 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. +# There are no plans to make this stable. + #! ### Chip selection features lpc55 = ["lpc55-pac"] diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs new file mode 100644 index 000000000..c95218af0 --- /dev/null +++ b/embassy-nxp/src/chips/lpc55.rs @@ -0,0 +1,70 @@ +pub use lpc55_pac as pac; + +embassy_hal_internal::peripherals! { + // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other + // peripheral types (e.g. I2C). + PIO0_0, + PIO0_1, + PIO0_2, + PIO0_3, + PIO0_4, + PIO0_5, + PIO0_6, + PIO0_7, + PIO0_8, + PIO0_9, + PIO0_10, + PIO0_11, + PIO0_12, + PIO0_13, + PIO0_14, + PIO0_15, + PIO0_16, + PIO0_17, + PIO0_18, + PIO0_19, + PIO0_20, + PIO0_21, + PIO0_22, + PIO0_23, + PIO0_24, + PIO0_25, + PIO0_26, + PIO0_27, + PIO0_28, + PIO0_29, + PIO0_30, + PIO0_31, + PIO1_0, + PIO1_1, + PIO1_2, + PIO1_3, + PIO1_4, + PIO1_5, + PIO1_6, + PIO1_7, + PIO1_8, + PIO1_9, + PIO1_10, + PIO1_11, + PIO1_12, + PIO1_13, + PIO1_14, + PIO1_15, + PIO1_16, + PIO1_17, + PIO1_18, + PIO1_19, + PIO1_20, + PIO1_21, + PIO1_22, + PIO1_23, + PIO1_24, + PIO1_25, + PIO1_26, + PIO1_27, + PIO1_28, + PIO1_29, + PIO1_30, + PIO1_31, +} diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs index c7c78ce61..809903d97 100644 --- a/embassy-nxp/src/gpio.rs +++ b/embassy-nxp/src/gpio.rs @@ -1,354 +1,5 @@ -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +//! General purpose input/output (GPIO) driver. -use crate::pac_utils::*; -use crate::{peripherals, Peri}; - -pub(crate) fn init() { - // Enable clocks for GPIO, PINT, and IOCON - syscon_reg() - .ahbclkctrl0 - .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); -} - -/// The GPIO pin level for pins set on "Digital" mode. -#[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum Level { - /// Logical low. Corresponds to 0V. - Low, - /// Logical high. Corresponds to VDD. - High, -} - -/// Pull setting for a GPIO input set on "Digital" mode. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum Pull { - /// No pull. - None, - /// Internal pull-up resistor. - Up, - /// Internal pull-down resistor. - Down, -} - -/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. -#[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum Bank { - Bank0 = 0, - Bank1 = 1, -} - -/// GPIO output driver. Internally, this is a specialized [Flex] pin. -pub struct Output<'d> { - pub(crate) pin: Flex<'d>, -} - -impl<'d> Output<'d> { - /// Create GPIO output driver for a [Pin] with the provided [initial output](Level). - #[inline] - pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { - let mut pin = Flex::new(pin); - pin.set_as_output(); - let mut result = Self { pin }; - - match initial_output { - Level::High => result.set_high(), - Level::Low => result.set_low(), - }; - - result - } - - pub fn set_high(&mut self) { - gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) - } - - pub fn set_low(&mut self) { - gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) - } - - pub fn toggle(&mut self) { - gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) - } - - /// Get the current output level of the pin. Note that the value returned by this function is - /// the voltage level reported by the pin, not the value set by the output driver. - pub fn level(&self) -> Level { - let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); - if bits & self.pin.bit() != 0 { - Level::High - } else { - Level::Low - } - } -} - -/// GPIO input driver. Internally, this is a specialized [Flex] pin. -pub struct Input<'d> { - pub(crate) pin: Flex<'d>, -} - -impl<'d> Input<'d> { - /// Create GPIO output driver for a [Pin] with the provided [Pull]. - #[inline] - pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { - let mut pin = Flex::new(pin); - pin.set_as_input(); - let mut result = Self { pin }; - result.set_pull(pull); - - result - } - - /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. - pub fn set_pull(&mut self, pull: Pull) { - match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { - register.modify(|_, w| match pull { - Pull::None => w.mode().inactive(), - Pull::Up => w.mode().pull_up(), - Pull::Down => w.mode().pull_down(), - }); - }); - } - - /// Get the current input level of the pin. - pub fn read(&self) -> Level { - let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); - if bits & self.pin.bit() != 0 { - Level::High - } else { - Level::Low - } - } -} - -/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a -/// reference to a type-erased pin called ["AnyPin"](AnyPin). -pub struct Flex<'d> { - pub(crate) pin: Peri<'d, AnyPin>, -} - -impl<'d> Flex<'d> { - /// Wrap the pin in a `Flex`. - /// - /// Note: you cannot assume that the pin will be in Digital mode after this call. - #[inline] - pub fn new(pin: Peri<'d, impl Pin>) -> Self { - Self { pin: pin.into() } - } - - /// Get the bank of this pin. See also [Bank]. - /// - /// # Example - /// - /// ``` - /// use embassy_nxp::gpio::{Bank, Flex}; - /// - /// let p = embassy_nxp::init(Default::default()); - /// let pin = Flex::new(p.PIO1_15); - /// - /// assert_eq!(pin.pin_bank(), Bank::Bank1); - /// ``` - pub fn pin_bank(&self) -> Bank { - self.pin.pin_bank() - } - - /// Get the number of this pin within its bank. See also [Bank]. - /// - /// # Example - /// - /// ``` - /// use embassy_nxp::gpio::Flex; - /// - /// let p = embassy_nxp::init(Default::default()); - /// let pin = Flex::new(p.PIO1_15); - /// - /// assert_eq!(pin.pin_number(), 15 as u8); - /// ``` - pub fn pin_number(&self) -> u8 { - self.pin.pin_number() - } - - /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note: - /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc. - /// - /// # Example - /// - /// ``` - /// use embassy_nxp::gpio::Flex; - /// - /// let p = embassy_nxp::init(Default::default()); - /// let pin = Flex::new(p.PIO1_3); - /// - /// assert_eq!(pin.bit(), 0b0000_1000); - /// ``` - pub fn bit(&self) -> u32 { - 1 << self.pin.pin_number() - } - - /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default - /// setting for pins is (usually) non-digital. - fn set_as_digital(&mut self) { - match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { - register.modify(|_, w| w.digimode().digital()); - }); - } - - /// Set the pin in output mode. This implies setting the pin to digital mode, which this - /// function handles itself. - pub fn set_as_output(&mut self) { - self.set_as_digital(); - gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) - } - - pub fn set_as_input(&mut self) { - self.set_as_digital(); - gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) - } -} - -/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. -pub(crate) trait SealedPin: Sized { - fn pin_bank(&self) -> Bank; - fn pin_number(&self) -> u8; -} - -/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an -/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the -/// `embassy-nxp` crate due to the [SealedPin] trait. -#[allow(private_bounds)] -pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { - /// Returns the pin number within a bank - #[inline] - fn pin(&self) -> u8 { - self.pin_number() - } - - /// Returns the bank of this pin - #[inline] - fn bank(&self) -> Bank { - self.pin_bank() - } -} - -/// Type-erased GPIO pin. -pub struct AnyPin { - pin_bank: Bank, - pin_number: u8, -} - -impl AnyPin { - /// Unsafely create a new type-erased pin. - /// - /// # Safety - /// - /// You must ensure that you’re only using one instance of this type at a time. - pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> { - Peri::new_unchecked(Self { pin_bank, pin_number }) - } -} - -impl_peripheral!(AnyPin); - -impl Pin for AnyPin {} -impl SealedPin for AnyPin { - #[inline] - fn pin_bank(&self) -> Bank { - self.pin_bank - } - - #[inline] - fn pin_number(&self) -> u8 { - self.pin_number - } -} - -macro_rules! impl_pin { - ($name:ident, $bank:expr, $pin_num:expr) => { - impl Pin for peripherals::$name {} - impl SealedPin for peripherals::$name { - #[inline] - fn pin_bank(&self) -> Bank { - $bank - } - - #[inline] - fn pin_number(&self) -> u8 { - $pin_num - } - } - - impl From for crate::gpio::AnyPin { - fn from(val: peripherals::$name) -> Self { - Self { - pin_bank: val.pin_bank(), - pin_number: val.pin_number(), - } - } - } - }; -} - -impl_pin!(PIO0_0, Bank::Bank0, 0); -impl_pin!(PIO0_1, Bank::Bank0, 1); -impl_pin!(PIO0_2, Bank::Bank0, 2); -impl_pin!(PIO0_3, Bank::Bank0, 3); -impl_pin!(PIO0_4, Bank::Bank0, 4); -impl_pin!(PIO0_5, Bank::Bank0, 5); -impl_pin!(PIO0_6, Bank::Bank0, 6); -impl_pin!(PIO0_7, Bank::Bank0, 7); -impl_pin!(PIO0_8, Bank::Bank0, 8); -impl_pin!(PIO0_9, Bank::Bank0, 9); -impl_pin!(PIO0_10, Bank::Bank0, 10); -impl_pin!(PIO0_11, Bank::Bank0, 11); -impl_pin!(PIO0_12, Bank::Bank0, 12); -impl_pin!(PIO0_13, Bank::Bank0, 13); -impl_pin!(PIO0_14, Bank::Bank0, 14); -impl_pin!(PIO0_15, Bank::Bank0, 15); -impl_pin!(PIO0_16, Bank::Bank0, 16); -impl_pin!(PIO0_17, Bank::Bank0, 17); -impl_pin!(PIO0_18, Bank::Bank0, 18); -impl_pin!(PIO0_19, Bank::Bank0, 19); -impl_pin!(PIO0_20, Bank::Bank0, 20); -impl_pin!(PIO0_21, Bank::Bank0, 21); -impl_pin!(PIO0_22, Bank::Bank0, 22); -impl_pin!(PIO0_23, Bank::Bank0, 23); -impl_pin!(PIO0_24, Bank::Bank0, 24); -impl_pin!(PIO0_25, Bank::Bank0, 25); -impl_pin!(PIO0_26, Bank::Bank0, 26); -impl_pin!(PIO0_27, Bank::Bank0, 27); -impl_pin!(PIO0_28, Bank::Bank0, 28); -impl_pin!(PIO0_29, Bank::Bank0, 29); -impl_pin!(PIO0_30, Bank::Bank0, 30); -impl_pin!(PIO0_31, Bank::Bank0, 31); -impl_pin!(PIO1_0, Bank::Bank1, 0); -impl_pin!(PIO1_1, Bank::Bank1, 1); -impl_pin!(PIO1_2, Bank::Bank1, 2); -impl_pin!(PIO1_3, Bank::Bank1, 3); -impl_pin!(PIO1_4, Bank::Bank1, 4); -impl_pin!(PIO1_5, Bank::Bank1, 5); -impl_pin!(PIO1_6, Bank::Bank1, 6); -impl_pin!(PIO1_7, Bank::Bank1, 7); -impl_pin!(PIO1_8, Bank::Bank1, 8); -impl_pin!(PIO1_9, Bank::Bank1, 9); -impl_pin!(PIO1_10, Bank::Bank1, 10); -impl_pin!(PIO1_11, Bank::Bank1, 11); -impl_pin!(PIO1_12, Bank::Bank1, 12); -impl_pin!(PIO1_13, Bank::Bank1, 13); -impl_pin!(PIO1_14, Bank::Bank1, 14); -impl_pin!(PIO1_15, Bank::Bank1, 15); -impl_pin!(PIO1_16, Bank::Bank1, 16); -impl_pin!(PIO1_17, Bank::Bank1, 17); -impl_pin!(PIO1_18, Bank::Bank1, 18); -impl_pin!(PIO1_19, Bank::Bank1, 19); -impl_pin!(PIO1_20, Bank::Bank1, 20); -impl_pin!(PIO1_21, Bank::Bank1, 21); -impl_pin!(PIO1_22, Bank::Bank1, 22); -impl_pin!(PIO1_23, Bank::Bank1, 23); -impl_pin!(PIO1_24, Bank::Bank1, 24); -impl_pin!(PIO1_25, Bank::Bank1, 25); -impl_pin!(PIO1_26, Bank::Bank1, 26); -impl_pin!(PIO1_27, Bank::Bank1, 27); -impl_pin!(PIO1_28, Bank::Bank1, 28); -impl_pin!(PIO1_29, Bank::Bank1, 29); -impl_pin!(PIO1_30, Bank::Bank1, 30); -impl_pin!(PIO1_31, Bank::Bank1, 31); +#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] +mod inner; +pub use inner::*; diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs new file mode 100644 index 000000000..94cd8b7f8 --- /dev/null +++ b/embassy-nxp/src/gpio/lpc55.rs @@ -0,0 +1,677 @@ +use embassy_hal_internal::{impl_peripheral, PeripheralType}; + +use crate::{peripherals, Peri}; + +pub(crate) fn init() { + // Enable clocks for GPIO, PINT, and IOCON + syscon_reg() + .ahbclkctrl0 + .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); +} + +/// The GPIO pin level for pins set on "Digital" mode. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Level { + /// Logical low. Corresponds to 0V. + Low, + /// Logical high. Corresponds to VDD. + High, +} + +/// Pull setting for a GPIO input set on "Digital" mode. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Pull { + /// No pull. + None, + /// Internal pull-up resistor. + Up, + /// Internal pull-down resistor. + Down, +} + +/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Bank { + Bank0 = 0, + Bank1 = 1, +} + +/// GPIO output driver. Internally, this is a specialized [Flex] pin. +pub struct Output<'d> { + pub(crate) pin: Flex<'d>, +} + +impl<'d> Output<'d> { + /// Create GPIO output driver for a [Pin] with the provided [initial output](Level). + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_output(); + let mut result = Self { pin }; + + match initial_output { + Level::High => result.set_high(), + Level::Low => result.set_low(), + }; + + result + } + + pub fn set_high(&mut self) { + gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + } + + pub fn set_low(&mut self) { + gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + } + + pub fn toggle(&mut self) { + gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + } + + /// Get the current output level of the pin. Note that the value returned by this function is + /// the voltage level reported by the pin, not the value set by the output driver. + pub fn level(&self) -> Level { + let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); + if bits & self.pin.bit() != 0 { + Level::High + } else { + Level::Low + } + } +} + +/// GPIO input driver. Internally, this is a specialized [Flex] pin. +pub struct Input<'d> { + pub(crate) pin: Flex<'d>, +} + +impl<'d> Input<'d> { + /// Create GPIO output driver for a [Pin] with the provided [Pull]. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_input(); + let mut result = Self { pin }; + result.set_pull(pull); + + result + } + + /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. + pub fn set_pull(&mut self, pull: Pull) { + match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { + register.modify(|_, w| match pull { + Pull::None => w.mode().inactive(), + Pull::Up => w.mode().pull_up(), + Pull::Down => w.mode().pull_down(), + }); + }); + } + + /// Get the current input level of the pin. + pub fn read(&self) -> Level { + let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); + if bits & self.pin.bit() != 0 { + Level::High + } else { + Level::Low + } + } +} + +/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a +/// reference to a type-erased pin called ["AnyPin"](AnyPin). +pub struct Flex<'d> { + pub(crate) pin: Peri<'d, AnyPin>, +} + +impl<'d> Flex<'d> { + /// Wrap the pin in a `Flex`. + /// + /// Note: you cannot assume that the pin will be in Digital mode after this call. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>) -> Self { + Self { pin: pin.into() } + } + + /// Get the bank of this pin. See also [Bank]. + /// + /// # Example + /// + /// ``` + /// use embassy_nxp::gpio::{Bank, Flex}; + /// + /// let p = embassy_nxp::init(Default::default()); + /// let pin = Flex::new(p.PIO1_15); + /// + /// assert_eq!(pin.pin_bank(), Bank::Bank1); + /// ``` + pub fn pin_bank(&self) -> Bank { + self.pin.pin_bank() + } + + /// Get the number of this pin within its bank. See also [Bank]. + /// + /// # Example + /// + /// ``` + /// use embassy_nxp::gpio::Flex; + /// + /// let p = embassy_nxp::init(Default::default()); + /// let pin = Flex::new(p.PIO1_15); + /// + /// assert_eq!(pin.pin_number(), 15 as u8); + /// ``` + pub fn pin_number(&self) -> u8 { + self.pin.pin_number() + } + + /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note: + /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc. + /// + /// # Example + /// + /// ``` + /// use embassy_nxp::gpio::Flex; + /// + /// let p = embassy_nxp::init(Default::default()); + /// let pin = Flex::new(p.PIO1_3); + /// + /// assert_eq!(pin.bit(), 0b0000_1000); + /// ``` + pub fn bit(&self) -> u32 { + 1 << self.pin.pin_number() + } + + /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default + /// setting for pins is (usually) non-digital. + fn set_as_digital(&mut self) { + match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { + register.modify(|_, w| w.digimode().digital()); + }); + } + + /// Set the pin in output mode. This implies setting the pin to digital mode, which this + /// function handles itself. + pub fn set_as_output(&mut self) { + self.set_as_digital(); + gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) + } + + pub fn set_as_input(&mut self) { + self.set_as_digital(); + gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) + } +} + +/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. +pub(crate) trait SealedPin: Sized { + fn pin_bank(&self) -> Bank; + fn pin_number(&self) -> u8; +} + +/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an +/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the +/// `embassy-nxp` crate due to the [SealedPin] trait. +#[allow(private_bounds)] +pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { + /// Returns the pin number within a bank + #[inline] + fn pin(&self) -> u8 { + self.pin_number() + } + + /// Returns the bank of this pin + #[inline] + fn bank(&self) -> Bank { + self.pin_bank() + } +} + +/// Type-erased GPIO pin. +pub struct AnyPin { + pin_bank: Bank, + pin_number: u8, +} + +impl AnyPin { + /// Unsafely create a new type-erased pin. + /// + /// # Safety + /// + /// You must ensure that you’re only using one instance of this type at a time. + pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> { + Peri::new_unchecked(Self { pin_bank, pin_number }) + } +} + +impl_peripheral!(AnyPin); + +impl Pin for AnyPin {} +impl SealedPin for AnyPin { + #[inline] + fn pin_bank(&self) -> Bank { + self.pin_bank + } + + #[inline] + fn pin_number(&self) -> u8 { + self.pin_number + } +} + +/// Get the GPIO register block. This is used to configure all GPIO pins. +/// +/// # Safety +/// Due to the type system of peripherals, access to the settings of a single pin is possible only +/// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You +/// must ensure that the GPIO registers are not accessed concurrently by multiple threads. +pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock { + unsafe { &*lpc55_pac::GPIO::ptr() } +} + +/// Get the IOCON register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock { + unsafe { &*lpc55_pac::IOCON::ptr() } +} + +/// Get the INPUTMUX register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock { + unsafe { &*lpc55_pac::INPUTMUX::ptr() } +} + +/// Get the SYSCON register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock { + unsafe { &*lpc55_pac::SYSCON::ptr() } +} + +/// Get the PINT register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { + unsafe { &*lpc55_pac::PINT::ptr() } +} + +/// Match the pin bank and number of a pin to the corresponding IOCON register. +/// +/// # Example +/// ``` +/// use embassy_nxp::gpio::Bank; +/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; +/// +/// // Make pin PIO1_6 digital and set it to pull-down mode. +/// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, { +/// register.modify(|_, w| w.mode().pull_down().digimode().digital()); +/// }); +/// ``` +macro_rules! match_iocon { + ($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => { + match ($pin_bank, $pin_number) { + (Bank::Bank0, 0) => { + let $register = &($iocon_register).pio0_0; + $action; + } + (Bank::Bank0, 1) => { + let $register = &($iocon_register).pio0_1; + $action; + } + (Bank::Bank0, 2) => { + let $register = &($iocon_register).pio0_2; + $action; + } + (Bank::Bank0, 3) => { + let $register = &($iocon_register).pio0_3; + $action; + } + (Bank::Bank0, 4) => { + let $register = &($iocon_register).pio0_4; + $action; + } + (Bank::Bank0, 5) => { + let $register = &($iocon_register).pio0_5; + $action; + } + (Bank::Bank0, 6) => { + let $register = &($iocon_register).pio0_6; + $action; + } + (Bank::Bank0, 7) => { + let $register = &($iocon_register).pio0_7; + $action; + } + (Bank::Bank0, 8) => { + let $register = &($iocon_register).pio0_8; + $action; + } + (Bank::Bank0, 9) => { + let $register = &($iocon_register).pio0_9; + $action; + } + (Bank::Bank0, 10) => { + let $register = &($iocon_register).pio0_10; + $action; + } + (Bank::Bank0, 11) => { + let $register = &($iocon_register).pio0_11; + $action; + } + (Bank::Bank0, 12) => { + let $register = &($iocon_register).pio0_12; + $action; + } + (Bank::Bank0, 13) => { + let $register = &($iocon_register).pio0_13; + $action; + } + (Bank::Bank0, 14) => { + let $register = &($iocon_register).pio0_14; + $action; + } + (Bank::Bank0, 15) => { + let $register = &($iocon_register).pio0_15; + $action; + } + (Bank::Bank0, 16) => { + let $register = &($iocon_register).pio0_16; + $action; + } + (Bank::Bank0, 17) => { + let $register = &($iocon_register).pio0_17; + $action; + } + (Bank::Bank0, 18) => { + let $register = &($iocon_register).pio0_18; + $action; + } + (Bank::Bank0, 19) => { + let $register = &($iocon_register).pio0_19; + $action; + } + (Bank::Bank0, 20) => { + let $register = &($iocon_register).pio0_20; + $action; + } + (Bank::Bank0, 21) => { + let $register = &($iocon_register).pio0_21; + $action; + } + (Bank::Bank0, 22) => { + let $register = &($iocon_register).pio0_22; + $action; + } + (Bank::Bank0, 23) => { + let $register = &($iocon_register).pio0_23; + $action; + } + (Bank::Bank0, 24) => { + let $register = &($iocon_register).pio0_24; + $action; + } + (Bank::Bank0, 25) => { + let $register = &($iocon_register).pio0_25; + $action; + } + (Bank::Bank0, 26) => { + let $register = &($iocon_register).pio0_26; + $action; + } + (Bank::Bank0, 27) => { + let $register = &($iocon_register).pio0_27; + $action; + } + (Bank::Bank0, 28) => { + let $register = &($iocon_register).pio0_28; + $action; + } + (Bank::Bank0, 29) => { + let $register = &($iocon_register).pio0_29; + $action; + } + (Bank::Bank0, 30) => { + let $register = &($iocon_register).pio0_30; + $action; + } + (Bank::Bank0, 31) => { + let $register = &($iocon_register).pio0_31; + $action; + } + (Bank::Bank1, 0) => { + let $register = &($iocon_register).pio1_0; + $action; + } + (Bank::Bank1, 1) => { + let $register = &($iocon_register).pio1_1; + $action; + } + (Bank::Bank1, 2) => { + let $register = &($iocon_register).pio1_2; + $action; + } + (Bank::Bank1, 3) => { + let $register = &($iocon_register).pio1_3; + $action; + } + (Bank::Bank1, 4) => { + let $register = &($iocon_register).pio1_4; + $action; + } + (Bank::Bank1, 5) => { + let $register = &($iocon_register).pio1_5; + $action; + } + (Bank::Bank1, 6) => { + let $register = &($iocon_register).pio1_6; + $action; + } + (Bank::Bank1, 7) => { + let $register = &($iocon_register).pio1_7; + $action; + } + (Bank::Bank1, 8) => { + let $register = &($iocon_register).pio1_8; + $action; + } + (Bank::Bank1, 9) => { + let $register = &($iocon_register).pio1_9; + $action; + } + (Bank::Bank1, 10) => { + let $register = &($iocon_register).pio1_10; + $action; + } + (Bank::Bank1, 11) => { + let $register = &($iocon_register).pio1_11; + $action; + } + (Bank::Bank1, 12) => { + let $register = &($iocon_register).pio1_12; + $action; + } + (Bank::Bank1, 13) => { + let $register = &($iocon_register).pio1_13; + $action; + } + (Bank::Bank1, 14) => { + let $register = &($iocon_register).pio1_14; + $action; + } + (Bank::Bank1, 15) => { + let $register = &($iocon_register).pio1_15; + $action; + } + (Bank::Bank1, 16) => { + let $register = &($iocon_register).pio1_16; + $action; + } + (Bank::Bank1, 17) => { + let $register = &($iocon_register).pio1_17; + $action; + } + (Bank::Bank1, 18) => { + let $register = &($iocon_register).pio1_18; + $action; + } + (Bank::Bank1, 19) => { + let $register = &($iocon_register).pio1_19; + $action; + } + (Bank::Bank1, 20) => { + let $register = &($iocon_register).pio1_20; + $action; + } + (Bank::Bank1, 21) => { + let $register = &($iocon_register).pio1_21; + $action; + } + (Bank::Bank1, 22) => { + let $register = &($iocon_register).pio1_22; + $action; + } + (Bank::Bank1, 23) => { + let $register = &($iocon_register).pio1_23; + $action; + } + (Bank::Bank1, 24) => { + let $register = &($iocon_register).pio1_24; + $action; + } + (Bank::Bank1, 25) => { + let $register = &($iocon_register).pio1_25; + $action; + } + (Bank::Bank1, 26) => { + let $register = &($iocon_register).pio1_26; + $action; + } + (Bank::Bank1, 27) => { + let $register = &($iocon_register).pio1_27; + $action; + } + (Bank::Bank1, 28) => { + let $register = &($iocon_register).pio1_28; + $action; + } + (Bank::Bank1, 29) => { + let $register = &($iocon_register).pio1_29; + $action; + } + (Bank::Bank1, 30) => { + let $register = &($iocon_register).pio1_30; + $action; + } + (Bank::Bank1, 31) => { + let $register = &($iocon_register).pio1_31; + $action; + } + _ => unreachable!(), + } + }; +} + +pub(crate) use match_iocon; + +macro_rules! impl_pin { + ($name:ident, $bank:expr, $pin_num:expr) => { + impl Pin for peripherals::$name {} + impl SealedPin for peripherals::$name { + #[inline] + fn pin_bank(&self) -> Bank { + $bank + } + + #[inline] + fn pin_number(&self) -> u8 { + $pin_num + } + } + + impl From for crate::gpio::AnyPin { + fn from(val: peripherals::$name) -> Self { + Self { + pin_bank: val.pin_bank(), + pin_number: val.pin_number(), + } + } + } + }; +} + +impl_pin!(PIO0_0, Bank::Bank0, 0); +impl_pin!(PIO0_1, Bank::Bank0, 1); +impl_pin!(PIO0_2, Bank::Bank0, 2); +impl_pin!(PIO0_3, Bank::Bank0, 3); +impl_pin!(PIO0_4, Bank::Bank0, 4); +impl_pin!(PIO0_5, Bank::Bank0, 5); +impl_pin!(PIO0_6, Bank::Bank0, 6); +impl_pin!(PIO0_7, Bank::Bank0, 7); +impl_pin!(PIO0_8, Bank::Bank0, 8); +impl_pin!(PIO0_9, Bank::Bank0, 9); +impl_pin!(PIO0_10, Bank::Bank0, 10); +impl_pin!(PIO0_11, Bank::Bank0, 11); +impl_pin!(PIO0_12, Bank::Bank0, 12); +impl_pin!(PIO0_13, Bank::Bank0, 13); +impl_pin!(PIO0_14, Bank::Bank0, 14); +impl_pin!(PIO0_15, Bank::Bank0, 15); +impl_pin!(PIO0_16, Bank::Bank0, 16); +impl_pin!(PIO0_17, Bank::Bank0, 17); +impl_pin!(PIO0_18, Bank::Bank0, 18); +impl_pin!(PIO0_19, Bank::Bank0, 19); +impl_pin!(PIO0_20, Bank::Bank0, 20); +impl_pin!(PIO0_21, Bank::Bank0, 21); +impl_pin!(PIO0_22, Bank::Bank0, 22); +impl_pin!(PIO0_23, Bank::Bank0, 23); +impl_pin!(PIO0_24, Bank::Bank0, 24); +impl_pin!(PIO0_25, Bank::Bank0, 25); +impl_pin!(PIO0_26, Bank::Bank0, 26); +impl_pin!(PIO0_27, Bank::Bank0, 27); +impl_pin!(PIO0_28, Bank::Bank0, 28); +impl_pin!(PIO0_29, Bank::Bank0, 29); +impl_pin!(PIO0_30, Bank::Bank0, 30); +impl_pin!(PIO0_31, Bank::Bank0, 31); +impl_pin!(PIO1_0, Bank::Bank1, 0); +impl_pin!(PIO1_1, Bank::Bank1, 1); +impl_pin!(PIO1_2, Bank::Bank1, 2); +impl_pin!(PIO1_3, Bank::Bank1, 3); +impl_pin!(PIO1_4, Bank::Bank1, 4); +impl_pin!(PIO1_5, Bank::Bank1, 5); +impl_pin!(PIO1_6, Bank::Bank1, 6); +impl_pin!(PIO1_7, Bank::Bank1, 7); +impl_pin!(PIO1_8, Bank::Bank1, 8); +impl_pin!(PIO1_9, Bank::Bank1, 9); +impl_pin!(PIO1_10, Bank::Bank1, 10); +impl_pin!(PIO1_11, Bank::Bank1, 11); +impl_pin!(PIO1_12, Bank::Bank1, 12); +impl_pin!(PIO1_13, Bank::Bank1, 13); +impl_pin!(PIO1_14, Bank::Bank1, 14); +impl_pin!(PIO1_15, Bank::Bank1, 15); +impl_pin!(PIO1_16, Bank::Bank1, 16); +impl_pin!(PIO1_17, Bank::Bank1, 17); +impl_pin!(PIO1_18, Bank::Bank1, 18); +impl_pin!(PIO1_19, Bank::Bank1, 19); +impl_pin!(PIO1_20, Bank::Bank1, 20); +impl_pin!(PIO1_21, Bank::Bank1, 21); +impl_pin!(PIO1_22, Bank::Bank1, 22); +impl_pin!(PIO1_23, Bank::Bank1, 23); +impl_pin!(PIO1_24, Bank::Bank1, 24); +impl_pin!(PIO1_25, Bank::Bank1, 25); +impl_pin!(PIO1_26, Bank::Bank1, 26); +impl_pin!(PIO1_27, Bank::Bank1, 27); +impl_pin!(PIO1_28, Bank::Bank1, 28); +impl_pin!(PIO1_29, Bank::Bank1, 29); +impl_pin!(PIO1_30, Bank::Bank1, 30); +impl_pin!(PIO1_31, Bank::Bank1, 31); diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index ad2056c06..433aca9e0 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -1,11 +1,19 @@ #![no_std] pub mod gpio; -mod pac_utils; +#[cfg(feature = "lpc55")] pub mod pint; -pub use embassy_hal_internal::Peri; -pub use lpc55_pac as pac; +// This mod MUST go last, so that it sees all the `impl_foo!` macros +#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] +mod chip; + +#[cfg(feature = "unstable-pac")] +pub use chip::pac; +#[cfg(not(feature = "unstable-pac"))] +pub(crate) use chip::pac; +pub use chip::{peripherals, Peripherals}; +pub use embassy_hal_internal::{Peri, PeripheralType}; /// Initialize the `embassy-nxp` HAL with the provided configuration. /// @@ -13,81 +21,15 @@ pub use lpc55_pac as pac; /// /// This should only be called once and at startup, otherwise it panics. pub fn init(_config: config::Config) -> Peripherals { - gpio::init(); - pint::init(); + #[cfg(feature = "lpc55")] + { + gpio::init(); + pint::init(); + } crate::Peripherals::take() } -embassy_hal_internal::peripherals! { - // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other - // peripheral types (e.g. I2C). - PIO0_0, - PIO0_1, - PIO0_2, - PIO0_3, - PIO0_4, - PIO0_5, - PIO0_6, - PIO0_7, - PIO0_8, - PIO0_9, - PIO0_10, - PIO0_11, - PIO0_12, - PIO0_13, - PIO0_14, - PIO0_15, - PIO0_16, - PIO0_17, - PIO0_18, - PIO0_19, - PIO0_20, - PIO0_21, - PIO0_22, - PIO0_23, - PIO0_24, - PIO0_25, - PIO0_26, - PIO0_27, - PIO0_28, - PIO0_29, - PIO0_30, - PIO0_31, - PIO1_0, - PIO1_1, - PIO1_2, - PIO1_3, - PIO1_4, - PIO1_5, - PIO1_6, - PIO1_7, - PIO1_8, - PIO1_9, - PIO1_10, - PIO1_11, - PIO1_12, - PIO1_13, - PIO1_14, - PIO1_15, - PIO1_16, - PIO1_17, - PIO1_18, - PIO1_19, - PIO1_20, - PIO1_21, - PIO1_22, - PIO1_23, - PIO1_24, - PIO1_25, - PIO1_26, - PIO1_27, - PIO1_28, - PIO1_29, - PIO1_30, - PIO1_31, -} - /// HAL configuration for the NXP board. pub mod config { #[derive(Default)] diff --git a/embassy-nxp/src/pac_utils.rs b/embassy-nxp/src/pac_utils.rs deleted file mode 100644 index 86a807f6c..000000000 --- a/embassy-nxp/src/pac_utils.rs +++ /dev/null @@ -1,323 +0,0 @@ -/// Get the GPIO register block. This is used to configure all GPIO pins. -/// -/// # Safety -/// Due to the type system of peripherals, access to the settings of a single pin is possible only -/// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You -/// must ensure that the GPIO registers are not accessed concurrently by multiple threads. -pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock { - unsafe { &*lpc55_pac::GPIO::ptr() } -} - -/// Get the IOCON register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock { - unsafe { &*lpc55_pac::IOCON::ptr() } -} - -/// Get the INPUTMUX register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock { - unsafe { &*lpc55_pac::INPUTMUX::ptr() } -} - -/// Get the SYSCON register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock { - unsafe { &*lpc55_pac::SYSCON::ptr() } -} - -/// Get the PINT register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { - unsafe { &*lpc55_pac::PINT::ptr() } -} - -/// Match the pin bank and number of a pin to the corresponding IOCON register. -/// -/// # Example -/// ``` -/// use embassy_nxp::gpio::Bank; -/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; -/// -/// // Make pin PIO1_6 digital and set it to pull-down mode. -/// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, { -/// register.modify(|_, w| w.mode().pull_down().digimode().digital()); -/// }); -/// ``` -macro_rules! match_iocon { - ($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => { - match ($pin_bank, $pin_number) { - (Bank::Bank0, 0) => { - let $register = &($iocon_register).pio0_0; - $action; - } - (Bank::Bank0, 1) => { - let $register = &($iocon_register).pio0_1; - $action; - } - (Bank::Bank0, 2) => { - let $register = &($iocon_register).pio0_2; - $action; - } - (Bank::Bank0, 3) => { - let $register = &($iocon_register).pio0_3; - $action; - } - (Bank::Bank0, 4) => { - let $register = &($iocon_register).pio0_4; - $action; - } - (Bank::Bank0, 5) => { - let $register = &($iocon_register).pio0_5; - $action; - } - (Bank::Bank0, 6) => { - let $register = &($iocon_register).pio0_6; - $action; - } - (Bank::Bank0, 7) => { - let $register = &($iocon_register).pio0_7; - $action; - } - (Bank::Bank0, 8) => { - let $register = &($iocon_register).pio0_8; - $action; - } - (Bank::Bank0, 9) => { - let $register = &($iocon_register).pio0_9; - $action; - } - (Bank::Bank0, 10) => { - let $register = &($iocon_register).pio0_10; - $action; - } - (Bank::Bank0, 11) => { - let $register = &($iocon_register).pio0_11; - $action; - } - (Bank::Bank0, 12) => { - let $register = &($iocon_register).pio0_12; - $action; - } - (Bank::Bank0, 13) => { - let $register = &($iocon_register).pio0_13; - $action; - } - (Bank::Bank0, 14) => { - let $register = &($iocon_register).pio0_14; - $action; - } - (Bank::Bank0, 15) => { - let $register = &($iocon_register).pio0_15; - $action; - } - (Bank::Bank0, 16) => { - let $register = &($iocon_register).pio0_16; - $action; - } - (Bank::Bank0, 17) => { - let $register = &($iocon_register).pio0_17; - $action; - } - (Bank::Bank0, 18) => { - let $register = &($iocon_register).pio0_18; - $action; - } - (Bank::Bank0, 19) => { - let $register = &($iocon_register).pio0_19; - $action; - } - (Bank::Bank0, 20) => { - let $register = &($iocon_register).pio0_20; - $action; - } - (Bank::Bank0, 21) => { - let $register = &($iocon_register).pio0_21; - $action; - } - (Bank::Bank0, 22) => { - let $register = &($iocon_register).pio0_22; - $action; - } - (Bank::Bank0, 23) => { - let $register = &($iocon_register).pio0_23; - $action; - } - (Bank::Bank0, 24) => { - let $register = &($iocon_register).pio0_24; - $action; - } - (Bank::Bank0, 25) => { - let $register = &($iocon_register).pio0_25; - $action; - } - (Bank::Bank0, 26) => { - let $register = &($iocon_register).pio0_26; - $action; - } - (Bank::Bank0, 27) => { - let $register = &($iocon_register).pio0_27; - $action; - } - (Bank::Bank0, 28) => { - let $register = &($iocon_register).pio0_28; - $action; - } - (Bank::Bank0, 29) => { - let $register = &($iocon_register).pio0_29; - $action; - } - (Bank::Bank0, 30) => { - let $register = &($iocon_register).pio0_30; - $action; - } - (Bank::Bank0, 31) => { - let $register = &($iocon_register).pio0_31; - $action; - } - (Bank::Bank1, 0) => { - let $register = &($iocon_register).pio1_0; - $action; - } - (Bank::Bank1, 1) => { - let $register = &($iocon_register).pio1_1; - $action; - } - (Bank::Bank1, 2) => { - let $register = &($iocon_register).pio1_2; - $action; - } - (Bank::Bank1, 3) => { - let $register = &($iocon_register).pio1_3; - $action; - } - (Bank::Bank1, 4) => { - let $register = &($iocon_register).pio1_4; - $action; - } - (Bank::Bank1, 5) => { - let $register = &($iocon_register).pio1_5; - $action; - } - (Bank::Bank1, 6) => { - let $register = &($iocon_register).pio1_6; - $action; - } - (Bank::Bank1, 7) => { - let $register = &($iocon_register).pio1_7; - $action; - } - (Bank::Bank1, 8) => { - let $register = &($iocon_register).pio1_8; - $action; - } - (Bank::Bank1, 9) => { - let $register = &($iocon_register).pio1_9; - $action; - } - (Bank::Bank1, 10) => { - let $register = &($iocon_register).pio1_10; - $action; - } - (Bank::Bank1, 11) => { - let $register = &($iocon_register).pio1_11; - $action; - } - (Bank::Bank1, 12) => { - let $register = &($iocon_register).pio1_12; - $action; - } - (Bank::Bank1, 13) => { - let $register = &($iocon_register).pio1_13; - $action; - } - (Bank::Bank1, 14) => { - let $register = &($iocon_register).pio1_14; - $action; - } - (Bank::Bank1, 15) => { - let $register = &($iocon_register).pio1_15; - $action; - } - (Bank::Bank1, 16) => { - let $register = &($iocon_register).pio1_16; - $action; - } - (Bank::Bank1, 17) => { - let $register = &($iocon_register).pio1_17; - $action; - } - (Bank::Bank1, 18) => { - let $register = &($iocon_register).pio1_18; - $action; - } - (Bank::Bank1, 19) => { - let $register = &($iocon_register).pio1_19; - $action; - } - (Bank::Bank1, 20) => { - let $register = &($iocon_register).pio1_20; - $action; - } - (Bank::Bank1, 21) => { - let $register = &($iocon_register).pio1_21; - $action; - } - (Bank::Bank1, 22) => { - let $register = &($iocon_register).pio1_22; - $action; - } - (Bank::Bank1, 23) => { - let $register = &($iocon_register).pio1_23; - $action; - } - (Bank::Bank1, 24) => { - let $register = &($iocon_register).pio1_24; - $action; - } - (Bank::Bank1, 25) => { - let $register = &($iocon_register).pio1_25; - $action; - } - (Bank::Bank1, 26) => { - let $register = &($iocon_register).pio1_26; - $action; - } - (Bank::Bank1, 27) => { - let $register = &($iocon_register).pio1_27; - $action; - } - (Bank::Bank1, 28) => { - let $register = &($iocon_register).pio1_28; - $action; - } - (Bank::Bank1, 29) => { - let $register = &($iocon_register).pio1_29; - $action; - } - (Bank::Bank1, 30) => { - let $register = &($iocon_register).pio1_30; - $action; - } - (Bank::Bank1, 31) => { - let $register = &($iocon_register).pio1_31; - $action; - } - _ => unreachable!(), - } - }; -} - -pub(crate) use match_iocon; diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index 8d6dc1277..dc117e7e3 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs @@ -7,9 +7,8 @@ use core::task::{Context, Poll}; use critical_section::Mutex; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::{self, AnyPin, Level, SealedPin}; +use crate::gpio::{self, inputmux_reg, pint_reg, syscon_reg, AnyPin, Level, SealedPin}; use crate::pac::interrupt; -use crate::pac_utils::*; use crate::Peri; struct PinInterrupt { -- cgit From e4aa539708781af2474240c5c16456ffe554754b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 9 Jul 2025 14:02:20 +0200 Subject: add embassy sync channel example for message passing between interrupt and task --- embassy-sync/src/channel.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index dda91c651..a0e39fcb5 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -17,6 +17,31 @@ //! messages that it can store, and if this limit is reached, trying to send //! another message will result in an error being returned. //! +//! # Example: Message passing between task and interrupt handler +//! +//! ```rust +//! use embassy_sync::channel::Channel; +//! use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +//! +//! static SHARED_CHANNEL: Channel = Channel::new(); +//! +//! fn my_interrupt_handler() { +//! // Do some work.. +//! // ... +//! if let Err(e) = SHARED_CHANNEL.sender().try_send(42) { +//! // Channel is full.. +//! } +//! } +//! +//! async fn my_async_task() { +//! // ... +//! let receiver = SHARED_CHANNEL.receiver(); +//! loop { +//! let data_from_interrupt = receiver.receive().await; +//! // Do something with the data. +//! } +//! } +//! ``` use core::cell::RefCell; use core::future::Future; -- cgit From 42c8379c5a571aa76214cdd73ef05a2c720eeb6e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 9 Jul 2025 14:21:19 +0200 Subject: some minor documentation fixes --- embassy-sync/src/mutex.rs | 2 +- embassy-sync/src/pipe.rs | 8 ++++---- embassy-sync/src/priority_channel.rs | 4 ++-- embassy-sync/src/pubsub/publisher.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 7528a9f68..67c682704 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -23,7 +23,7 @@ struct State { /// Async mutex. /// -/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). +/// The mutex is generic over a blocking [RawMutex]. /// The raw mutex is used to guard access to the internal "is locked" flag. It /// is held for very short periods only, while locking and unlocking. It is *not* held /// for the entire time the async Mutex is locked. diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 2598652d2..df3b28b45 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -152,7 +152,7 @@ where impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {} -/// Future returned by [`Pipe::fill_buf`] and [`Reader::fill_buf`]. +/// Future returned by [`Reader::fill_buf`]. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FillBufFuture<'p, M, const N: usize> where @@ -587,7 +587,7 @@ where } } -/// Write-only access to a [`DynamicPipe`]. +/// Write-only access to the dynamic pipe. pub struct DynamicWriter<'p> { pipe: &'p dyn DynamicPipe, } @@ -657,7 +657,7 @@ where } } -/// Read-only access to a [`DynamicPipe`]. +/// Read-only access to a dynamic pipe. pub struct DynamicReader<'p> { pipe: &'p dyn DynamicPipe, } @@ -742,7 +742,7 @@ where } } -/// Future returned by [`DynamicPipe::fill_buf`] and [`DynamicReader::fill_buf`]. +/// Future returned by [`DynamicReader::fill_buf`]. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DynamicFillBufFuture<'p> { pipe: Option<&'p dyn DynamicPipe>, diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index 623c52993..a6fbe8def 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs @@ -1,7 +1,7 @@ //! A queue for sending values between asynchronous tasks. //! //! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue. -//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`](heapless::binary_heap::Kind) parameter of the channel. +//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [Kind] parameter of the channel. use core::cell::RefCell; use core::future::Future; @@ -473,7 +473,7 @@ where /// received from the channel. /// /// Sent data may be reordered based on their priority within the channel. -/// For example, in a [`Max`](heapless::binary_heap::Max) [`PriorityChannel`] +/// For example, in a [Max][PriorityChannel] /// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. pub struct PriorityChannel where diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 7a1ab66de..2af1a9334 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -75,7 +75,7 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { self.channel.is_full() } - /// Create a [`futures::Sink`] adapter for this publisher. + /// Create a [futures_sink::Sink] adapter for this publisher. #[inline] pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { PubSink { publ: self, fut: None } -- cgit From da392ed942bbf78117f1dbba32208458de7cdea8 Mon Sep 17 00:00:00 2001 From: Robin Mueller <31589589+robamu@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:26:20 +0200 Subject: Update embassy-sync/src/mutex.rs Co-authored-by: James Munns --- embassy-sync/src/mutex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 67c682704..8496f34bf 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -23,7 +23,7 @@ struct State { /// Async mutex. /// -/// The mutex is generic over a blocking [RawMutex]. +/// The mutex is generic over a blocking [`RawMutex`]. /// The raw mutex is used to guard access to the internal "is locked" flag. It /// is held for very short periods only, while locking and unlocking. It is *not* held /// for the entire time the async Mutex is locked. -- cgit From 9892963da946aa896d9387916ee2b5d509b63e3b Mon Sep 17 00:00:00 2001 From: Robin Mueller <31589589+robamu@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:28:18 +0200 Subject: Update embassy-sync/src/priority_channel.rs Co-authored-by: James Munns --- embassy-sync/src/priority_channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index a6fbe8def..6765a1503 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs @@ -1,7 +1,7 @@ //! A queue for sending values between asynchronous tasks. //! //! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue. -//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [Kind] parameter of the channel. +//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`] parameter of the channel. use core::cell::RefCell; use core::future::Future; -- cgit From 554fbef571cf9f4692d6361d66f962df926615df Mon Sep 17 00:00:00 2001 From: Robin Mueller <31589589+robamu@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:31:04 +0200 Subject: Update embassy-sync/src/priority_channel.rs Co-authored-by: James Munns --- embassy-sync/src/priority_channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index 6765a1503..715a20e86 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs @@ -473,7 +473,7 @@ where /// received from the channel. /// /// Sent data may be reordered based on their priority within the channel. -/// For example, in a [Max][PriorityChannel] +/// For example, in a [`Max`] [`PriorityChannel`] /// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. pub struct PriorityChannel where -- cgit From fa0f6bc670c29b799e0ef8812de041727c5d86f3 Mon Sep 17 00:00:00 2001 From: Robin Mueller <31589589+robamu@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:31:42 +0200 Subject: Update embassy-sync/src/pubsub/publisher.rs Co-authored-by: James Munns --- embassy-sync/src/pubsub/publisher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 2af1a9334..52a52f926 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -75,7 +75,7 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { self.channel.is_full() } - /// Create a [futures_sink::Sink] adapter for this publisher. + /// Create a [`futures_sink::Sink`] adapter for this publisher. #[inline] pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { PubSink { publ: self, fut: None } -- cgit From 9589c056d1963a2a342a4486181f3ce139ddeea5 Mon Sep 17 00:00:00 2001 From: dimi Date: Wed, 9 Jul 2025 20:00:49 +0200 Subject: fix typo --- embassy-stm32/src/timer/one_pulse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index c8f0cafe7..498d9c082 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -183,7 +183,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// as an output. pub fn new_ch2( tim: Peri<'d, T>, - _pin: TriggerPin<'d, T, Ch1>, + _pin: TriggerPin<'d, T, Ch2>, _irq: impl Binding> + 'd, freq: Hertz, pulse_end: u32, -- cgit From 423870d1f73e8b3110ae1ecc76989b112cb2da2b Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 10 Jul 2025 15:57:45 +0200 Subject: Introduce traits for special channels --- embassy-stm32/src/adc/g4.rs | 119 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 25 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 1fce3085a..cb3e342d8 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -24,44 +24,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); #[cfg(stm32h7)] const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); -#[cfg(stm32g4)] -const VREF_CHANNEL: u8 = 18; -#[cfg(stm32g4)] -const TEMP_CHANNEL: u8 = 16; - -#[cfg(stm32h7)] -const VREF_CHANNEL: u8 = 19; -#[cfg(stm32h7)] -const TEMP_CHANNEL: u8 = 18; - -// TODO this should be 14 for H7a/b/35 -const VBAT_CHANNEL: u8 = 17; - // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs /// Internal voltage reference channel. pub struct VrefInt; -impl AdcChannel for VrefInt {} -impl super::SealedAdcChannel for VrefInt { +impl AdcChannel for VrefInt {} +impl super::SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { - VREF_CHANNEL + T::CHANNEL } } /// Internal temperature channel. pub struct Temperature; -impl AdcChannel for Temperature {} -impl super::SealedAdcChannel for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { - TEMP_CHANNEL + T::CHANNEL } } /// Internal battery voltage channel. pub struct Vbat; -impl AdcChannel for Vbat {} -impl super::SealedAdcChannel for Vbat { +impl AdcChannel for Vbat {} +impl super::SealedAdcChannel for Vbat { fn channel(&self) -> u8 { - VBAT_CHANNEL + T::CHANNEL } } @@ -234,7 +221,10 @@ impl<'d, T: Instance> Adc<'d, T> { } /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> VrefInt { + pub fn enable_vrefint(&self) -> VrefInt + where + T: VrefChannel, + { T::common_regs().ccr().modify(|reg| { reg.set_vrefen(true); }); @@ -243,7 +233,10 @@ impl<'d, T: Instance> Adc<'d, T> { } /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> Temperature { + pub fn enable_temperature(&self) -> Temperature + where + T: TemperatureChannel, + { T::common_regs().ccr().modify(|reg| { reg.set_vsenseen(true); }); @@ -252,7 +245,10 @@ impl<'d, T: Instance> Adc<'d, T> { } /// Enable reading the vbat internal channel. - pub fn enable_vbat(&self) -> Vbat { + pub fn enable_vbat(&self) -> Vbat + where + T: VBatChannel, + { T::common_regs().ccr().modify(|reg| { reg.set_vbaten(true); }); @@ -519,3 +515,76 @@ impl<'d, T: Instance> Adc<'d, T> { } } } + +/// Implemented for ADCs that have a Temperature channel +pub trait TemperatureChannel { + const CHANNEL: u8; +} +/// Implemented for ADCs that have a Vref channel +pub trait VrefChannel { + const CHANNEL: u8; +} +/// Implemented for ADCs that have a VBat channel +pub trait VBatChannel { + const CHANNEL: u8; +} + +#[cfg(stm32g4)] +mod g4 { + pub use super::*; + + impl TemperatureChannel for crate::peripherals::ADC1 { + const CHANNEL: u8 = 16; + } + + impl VrefChannel for crate::peripherals::ADC1 { + const CHANNEL: u8 = 18; + } + + impl VBatChannel for crate::peripherals::ADC1 { + const CHANNEL: u8 = 17; + } + + impl VrefChannel for crate::peripherals::ADC3 { + const CHANNEL: u8 = 18; + } + + impl VBatChannel for crate::peripherals::ADC3 { + const CHANNEL: u8 = 17; + } + + #[cfg(not(stm32g4x1))] + impl VrefChannel for crate::peripherals::ADC4 { + const CHANNEL: u8 = 18; + } + + #[cfg(not(stm32g4x1))] + impl TemperatureChannel for crate::peripherals::ADC5 { + const CHANNEL: u8 = 4; + } + + #[cfg(not(stm32g4x1))] + impl VrefChannel for crate::peripherals::ADC5 { + const CHANNEL: u8 = 18; + } + + #[cfg(not(stm32g4x1))] + impl VBatChannel for crate::peripherals::ADC5 { + const CHANNEL: u8 = 17; + } +} + +// TODO this should look at each ADC individually and impl the correct channels +#[cfg(stm32h7)] +mod h7 { + impl TemperatureChannel for T { + const CHANNEL: u8 = 18; + } + impl VrefChannel for T { + const CHANNEL: u8 = 19; + } + impl VBatChannel for T { + // TODO this should be 14 for H7a/b/35 + const CHANNEL: u8 = 17; + } +} -- cgit From 7b7a62a0c255890f26dc59c17f2e3815b71455e7 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 10 Jul 2025 18:45:31 +0200 Subject: Fixing the nrf54l drive configuration bug --- 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 d02da9ac5..65f2d99f7 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -292,7 +292,7 @@ pub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive) } w.set_drive0(convert(drive.low)); - w.set_drive0(convert(drive.high)); + w.set_drive1(convert(drive.high)); } } -- cgit From a8e905f14ecf238675b343c037d20700ab6f4881 Mon Sep 17 00:00:00 2001 From: "robert.jeutter" Date: Thu, 5 Jun 2025 06:48:44 +0200 Subject: net-nrf91: add nrf9151 support. --- .github/ci/doc.sh | 1 + embassy-net-nrf91/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 58ffe5f2e..06c61f8c0 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -42,6 +42,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup +docserver-builder -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index 61fcaea1f..0bd9be0d9 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -119,14 +119,16 @@ async fn new_internal<'a>( let shmem_ptr = shmem.as_mut_ptr() as *mut u8; const SPU_REGION_SIZE: usize = 8192; // 8kb - assert!(shmem_len != 0); + trace!(" shmem_ptr = {}, shmem_len = {}", shmem_ptr, shmem_len); + + assert!(shmem_len != 0, "shmem length must not be zero"); assert!( shmem_len % SPU_REGION_SIZE == 0, "shmem length must be a multiple of 8kb" ); assert!( (shmem_ptr as usize) % SPU_REGION_SIZE == 0, - "shmem length must be a multiple of 8kb" + "shmem pointer must be 8kb-aligned" ); assert!( (shmem_ptr as usize + shmem_len) < 0x2002_0000, @@ -135,8 +137,15 @@ async fn new_internal<'a>( let spu = pac::SPU_S; debug!("Setting IPC RAM as nonsecure..."); + trace!( + " SPU_REGION_SIZE={}, shmem_ptr=0x{:08X}, shmem_len={}", + SPU_REGION_SIZE, + shmem_ptr as usize, + shmem_len + ); let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE; let region_end = region_start + shmem_len / SPU_REGION_SIZE; + trace!(" region_start={}, region_end={}", region_start, region_end); for i in region_start..region_end { spu.ramregion(i).perm().write(|w| { w.set_execute(true); @@ -154,13 +163,18 @@ async fn new_internal<'a>( end: unsafe { shmem_ptr.add(shmem_len) }, _phantom: PhantomData, }; - - let ipc = pac::IPC_NS; - let power = pac::POWER_S; + trace!( + " Allocator: start=0x{:08X}, end=0x{:08X}", + alloc.start as usize, + alloc.end as usize + ); let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() }); + let rx = alloc.alloc_bytes(RX_SIZE); + trace!(" RX buffer at {}, size={}", rx.as_ptr(), RX_SIZE); let trace = alloc.alloc_bytes(TRACE_SIZE); + trace!(" Trace buffer at {}, size={}", trace.as_ptr(), TRACE_SIZE); cb.version = 0x00010000; cb.rx_base = rx.as_mut_ptr() as _; @@ -174,8 +188,10 @@ async fn new_internal<'a>( cb.trace.base = trace.as_mut_ptr() as _; cb.trace.size = TRACE_SIZE; + let ipc = pac::IPC_NS; ipc.gpmem(0).write_value(cb as *mut _ as u32); ipc.gpmem(1).write_value(0); + trace!(" GPMEM[0]={:#X}, GPMEM[1]={}", cb as *mut _ as u32, 0); // connect task/event i to channel i for i in 0..8 { @@ -185,8 +201,9 @@ async fn new_internal<'a>( compiler_fence(Ordering::SeqCst); + let power = pac::POWER_S; // POWER.LTEMODEM.STARTN = 0 - // The reg is missing in the PAC?? + // TODO: The reg is missing in the PAC?? let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) }; unsafe { startn.write_volatile(0) } @@ -202,6 +219,8 @@ async fn new_internal<'a>( rx_control_list: ptr::null_mut(), rx_data_list: ptr::null_mut(), + rx_control_len: 0, + rx_data_len: 0, rx_seq_no: 0, rx_check: PointerChecker { start: rx.as_mut_ptr() as *mut u8, @@ -310,6 +329,10 @@ struct StateInner { rx_control_list: *mut List, rx_data_list: *mut List, + /// Number of entries in the control list + rx_control_len: usize, + /// Number of entries in the data list + rx_data_len: usize, rx_seq_no: u16, rx_check: PointerChecker, @@ -346,8 +369,11 @@ impl StateInner { self.rx_data_list = desc.data_list_ptr; let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() }; let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() }; - assert_eq!(rx_control_len, LIST_LEN); - assert_eq!(rx_data_len, LIST_LEN); + + trace!("modem control list length: {}", rx_control_len); + trace!("modem data list length: {}", rx_data_len); + self.rx_control_len = rx_control_len; + self.rx_data_len = rx_data_len; self.init = true; debug!("IPC initialized OK!"); @@ -463,7 +489,12 @@ impl StateInner { fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner) -> bool { let mut did_work = false; - for i in 0..LIST_LEN { + let max = if is_control { + self.rx_control_len + } else { + self.rx_data_len + }; + for i in 0..max { let item_ptr = unsafe { addr_of_mut!((*list).items[i]) }; let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() }; if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 { @@ -947,7 +978,7 @@ impl<'a> Runner<'a> { } } -const LIST_LEN: usize = 16; +const LIST_LEN: usize = 32; #[repr(C)] struct ControlBlock { -- cgit From b666a88ab175043d711c97b67b5b4d3bf409f102 Mon Sep 17 00:00:00 2001 From: korbin Date: Sun, 13 Jul 2025 20:30:26 -0600 Subject: make usb endpoint allocator methods accept an optional EndpointAddress --- embassy-nrf/src/usb/mod.rs | 51 +++++++++++++++++++++++-------- embassy-rp/src/usb.rs | 30 ++++++++++++++----- embassy-stm32/src/usb/otg.rs | 8 +++-- embassy-stm32/src/usb/usb.rs | 56 ++++++++++++++++++++++++++++------- embassy-usb-driver/src/lib.rs | 2 ++ embassy-usb-synopsys-otg/src/lib.rs | 43 +++++++++++++++++++-------- embassy-usb/src/builder.rs | 47 ++++++++++++++++++++++------- embassy-usb/src/class/cdc_acm.rs | 6 ++-- embassy-usb/src/class/cdc_ncm/mod.rs | 6 ++-- embassy-usb/src/class/cmsis_dap_v2.rs | 6 ++-- embassy-usb/src/class/hid.rs | 4 +-- embassy-usb/src/class/midi.rs | 4 +-- embassy-usb/src/class/uac1/speaker.rs | 3 +- examples/rp/src/bin/usb_raw_bulk.rs | 4 +-- examples/rp/src/bin/usb_webusb.rs | 4 +-- examples/rp235x/src/bin/usb_webusb.rs | 4 +-- 16 files changed, 204 insertions(+), 74 deletions(-) diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 6cc1b0111..c6970fc0f 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -121,10 +121,11 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V fn alloc_endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, packet_size: u16, interval_ms: u8, ) -> Result { - let index = self.alloc_in.allocate(ep_type)?; + let index = self.alloc_in.allocate(ep_type, ep_addr)?; let ep_addr = EndpointAddress::from_parts(index, Direction::In); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, @@ -137,10 +138,11 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, packet_size: u16, interval_ms: u8, ) -> Result { - let index = self.alloc_out.allocate(ep_type)?; + let index = self.alloc_out.allocate(ep_type, ep_addr)?; let ep_addr = EndpointAddress::from_parts(index, Direction::Out); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, @@ -734,7 +736,11 @@ impl Allocator { Self { used: 0 } } - fn allocate(&mut self, ep_type: EndpointType) -> Result { + fn allocate( + &mut self, + ep_type: EndpointType, + ep_addr: Option, + ) -> Result { // Endpoint addresses are fixed in hardware: // - 0x80 / 0x00 - Control EP0 // - 0x81 / 0x01 - Bulk/Interrupt EP1 @@ -748,16 +754,37 @@ impl Allocator { // Endpoint directions are allocated individually. - let alloc_index = match ep_type { - EndpointType::Isochronous => 8, - EndpointType::Control => return Err(driver::EndpointAllocError), - EndpointType::Interrupt | EndpointType::Bulk => { - // Find rightmost zero bit in 1..=7 - let ones = (self.used >> 1).trailing_ones() as usize; - if ones >= 7 { - return Err(driver::EndpointAllocError); + let alloc_index = if let Some(addr) = ep_addr { + // Use the specified endpoint address + let requested_index = addr.index(); + // Validate the requested index based on endpoint type + match ep_type { + EndpointType::Isochronous => { + if requested_index != 8 { + return Err(driver::EndpointAllocError); + } + } + EndpointType::Control => return Err(driver::EndpointAllocError), + EndpointType::Interrupt | EndpointType::Bulk => { + if requested_index < 1 || requested_index > 7 { + return Err(driver::EndpointAllocError); + } + } + } + requested_index + } else { + // Allocate any available endpoint + match ep_type { + EndpointType::Isochronous => 8, + EndpointType::Control => return Err(driver::EndpointAllocError), + EndpointType::Interrupt | EndpointType::Bulk => { + // Find rightmost zero bit in 1..=7 + let ones = (self.used >> 1).trailing_ones() as usize; + if ones >= 7 { + return Err(driver::EndpointAllocError); + } + ones + 1 } - ones + 1 } }; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 96541ade6..671ecbd32 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -153,6 +153,7 @@ impl<'d, T: Instance> Driver<'d, T> { fn alloc_endpoint( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result, driver::EndpointAllocError> { @@ -169,12 +170,25 @@ impl<'d, T: Instance> Driver<'d, T> { Direction::In => &mut self.ep_in, }; - let index = alloc.iter_mut().enumerate().find(|(i, ep)| { - if *i == 0 { - return false; // reserved for control pipe + let index = if let Some(addr) = ep_addr { + // Use the specified endpoint address + let requested_index = addr.index(); + if requested_index == 0 || requested_index >= EP_COUNT { + return Err(EndpointAllocError); } - !ep.used - }); + if alloc[requested_index].used { + return Err(EndpointAllocError); + } + Some((requested_index, &mut alloc[requested_index])) + } else { + // Find any available endpoint + alloc.iter_mut().enumerate().find(|(i, ep)| { + if *i == 0 { + return false; // reserved for control pipe + } + !ep.used + }) + }; let (index, ep) = index.ok_or(EndpointAllocError)?; assert!(!ep.used); @@ -299,19 +313,21 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { fn alloc_endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) } fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 590d1a427..3547ded00 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -231,19 +231,23 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { fn alloc_endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.inner.alloc_endpoint_in(ep_type, max_packet_size, interval_ms) + self.inner + .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.inner.alloc_endpoint_out(ep_type, max_packet_size, interval_ms) + self.inner + .alloc_endpoint_out(ep_type, ep_addr, 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 3e8e74a1f..05c28aceb 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -362,6 +362,7 @@ impl<'d, T: Instance> Driver<'d, T> { fn alloc_endpoint( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result, driver::EndpointAllocError> { @@ -373,25 +374,56 @@ impl<'d, T: Instance> Driver<'d, T> { D::dir() ); - let index = self.alloc.iter_mut().enumerate().find(|(i, ep)| { - if *i == 0 && ep_type != EndpointType::Control { - return false; // reserved for control pipe + let index = if let Some(addr) = ep_addr { + // Use the specified endpoint address + let requested_index = addr.index(); + if requested_index >= EP_COUNT { + return Err(EndpointAllocError); } + if requested_index == 0 && ep_type != EndpointType::Control { + return Err(EndpointAllocError); // EP0 is reserved for control + } + + let ep = &self.alloc[requested_index]; let used = ep.used_out || ep.used_in; if used && (ep.ep_type == EndpointType::Isochronous) { // Isochronous endpoints are always double-buffered. // Their corresponding endpoint/channel registers are forced to be unidirectional. // Do not reuse this index. - // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. - return false; + return Err(EndpointAllocError); } let used_dir = match D::dir() { Direction::Out => ep.used_out, Direction::In => ep.used_in, }; - !used || (ep.ep_type == ep_type && !used_dir) - }); + if used && (ep.ep_type != ep_type || used_dir) { + return Err(EndpointAllocError); + } + + Some((requested_index, &mut self.alloc[requested_index])) + } else { + // Find any available endpoint + self.alloc.iter_mut().enumerate().find(|(i, ep)| { + if *i == 0 && ep_type != EndpointType::Control { + return false; // reserved for control pipe + } + let used = ep.used_out || ep.used_in; + if used && (ep.ep_type == EndpointType::Isochronous) { + // Isochronous endpoints are always double-buffered. + // Their corresponding endpoint/channel registers are forced to be unidirectional. + // Do not reuse this index. + // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. + return false; + } + + let used_dir = match D::dir() { + Direction::Out => ep.used_out, + Direction::In => ep.used_in, + }; + !used || (ep.ep_type == ep_type && !used_dir) + }) + }; let (index, ep) = match index { Some(x) => x, @@ -479,27 +511,29 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { fn alloc_endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) } 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) + .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) .unwrap(); let ep_in = self - .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) .unwrap(); assert_eq!(ep_out.info.addr.index(), 0); assert_eq!(ep_in.info.addr.index(), 0); diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index d204e4d85..99616f1ec 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -136,6 +136,7 @@ pub trait Driver<'a> { fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result; @@ -153,6 +154,7 @@ pub trait Driver<'a> { fn alloc_endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result; diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index fc4428b54..7fc142a4e 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -345,6 +345,7 @@ impl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> { fn alloc_endpoint( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result, EndpointAllocError> { @@ -379,15 +380,31 @@ impl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> { Direction::In => &mut self.ep_in[..self.instance.endpoint_count], }; - // 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() + // Find endpoint slot + let slot = if let Some(addr) = ep_addr { + // Use the specified endpoint address + let requested_index = addr.index(); + if requested_index >= self.instance.endpoint_count { + return Err(EndpointAllocError); } - }); + if requested_index == 0 && ep_type != EndpointType::Control { + return Err(EndpointAllocError); // EP0 is reserved for control + } + if eps[requested_index].is_some() { + return Err(EndpointAllocError); // Already allocated + } + Some((requested_index, &mut eps[requested_index])) + } else { + // Find any free endpoint 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)) => { @@ -438,27 +455,29 @@ impl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Driver<'d> for Driver<'d fn alloc_endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) } 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) + .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) .unwrap(); let ep_in = self - .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) .unwrap(); assert_eq!(ep_out.info.addr.index(), 0); assert_eq!(ep_in.info.addr.index(), 0); diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6c4b3f9a4..8d7abe46c 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -2,7 +2,7 @@ use heapless::Vec; use crate::config::MAX_HANDLER_COUNT; use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType}; -use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType}; +use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType}; use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::{InterfaceNumber, StringIndex}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; @@ -465,11 +465,17 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// Allocate an IN endpoint, without writing its descriptor. /// /// Used for granular control over the order of endpoint and descriptor creation. - pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { + pub fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + ep_addr: Option, + max_packet_size: u16, + interval_ms: u8, + ) -> D::EndpointIn { let ep = self .builder .driver - .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) + .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms) .expect("alloc_endpoint_in failed"); ep @@ -478,13 +484,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { fn endpoint_in( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, synchronization_type: SynchronizationType, usage_type: UsageType, extra_fields: &[u8], ) -> D::EndpointIn { - let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms); + let ep = self.alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms); self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); ep @@ -496,13 +503,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { pub fn alloc_endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, ) -> D::EndpointOut { let ep = self .builder .driver - .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) + .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms) .expect("alloc_endpoint_out failed"); ep @@ -511,13 +519,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { fn endpoint_out( &mut self, ep_type: EndpointType, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, synchronization_type: SynchronizationType, usage_type: UsageType, extra_fields: &[u8], ) -> D::EndpointOut { - let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms); + let ep = self.alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms); self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); ep @@ -527,9 +536,10 @@ 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_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { + pub fn endpoint_bulk_in(&mut self, ep_addr: Option, max_packet_size: u16) -> D::EndpointIn { self.endpoint_in( EndpointType::Bulk, + ep_addr, max_packet_size, 0, SynchronizationType::NoSynchronization, @@ -542,9 +552,10 @@ 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_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { + pub fn endpoint_bulk_out(&mut self, ep_addr: Option, max_packet_size: u16) -> D::EndpointOut { self.endpoint_out( EndpointType::Bulk, + ep_addr, max_packet_size, 0, SynchronizationType::NoSynchronization, @@ -557,9 +568,15 @@ 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_ms: u8) -> D::EndpointIn { + pub fn endpoint_interrupt_in( + &mut self, + ep_addr: Option, + max_packet_size: u16, + interval_ms: u8, + ) -> D::EndpointIn { self.endpoint_in( EndpointType::Interrupt, + ep_addr, max_packet_size, interval_ms, SynchronizationType::NoSynchronization, @@ -569,9 +586,15 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { } /// Allocate a INTERRUPT OUT endpoint and write its descriptor. - pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { + pub fn endpoint_interrupt_out( + &mut self, + ep_addr: Option, + max_packet_size: u16, + interval_ms: u8, + ) -> D::EndpointOut { self.endpoint_out( EndpointType::Interrupt, + ep_addr, max_packet_size, interval_ms, SynchronizationType::NoSynchronization, @@ -586,6 +609,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// classes care about the order. pub fn endpoint_isochronous_in( &mut self, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, synchronization_type: SynchronizationType, @@ -594,6 +618,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { ) -> D::EndpointIn { self.endpoint_in( EndpointType::Isochronous, + ep_addr, max_packet_size, interval_ms, synchronization_type, @@ -605,6 +630,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. pub fn endpoint_isochronous_out( &mut self, + ep_addr: Option, max_packet_size: u16, interval_ms: u8, synchronization_type: SynchronizationType, @@ -613,6 +639,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { ) -> D::EndpointOut { self.endpoint_out( EndpointType::Isochronous, + ep_addr, max_packet_size, interval_ms, synchronization_type, diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 732a433f8..a1144ce05 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -254,14 +254,14 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { ], ); - let comm_ep = alt.endpoint_interrupt_in(8, 255); + let comm_ep = alt.endpoint_interrupt_in(None, 8, 255); // 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, None); - let read_ep = alt.endpoint_bulk_out(max_packet_size); - let write_ep = alt.endpoint_bulk_in(max_packet_size); + let read_ep = alt.endpoint_bulk_out(None, max_packet_size); + let write_ep = alt.endpoint_bulk_in(None, max_packet_size); drop(func); diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 09d923d2a..3af853091 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -313,15 +313,15 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { ], ); - let comm_ep = alt.endpoint_interrupt_in(8, 255); + let comm_ep = alt.endpoint_interrupt_in(None, 8, 255); // Data interface let mut iface = func.interface(); 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); + let read_ep = alt.endpoint_bulk_out(None, max_packet_size); + let write_ep = alt.endpoint_bulk_in(None, max_packet_size); drop(func); diff --git a/embassy-usb/src/class/cmsis_dap_v2.rs b/embassy-usb/src/class/cmsis_dap_v2.rs index a94e3ddb7..a9fd9cdf0 100644 --- a/embassy-usb/src/class/cmsis_dap_v2.rs +++ b/embassy-usb/src/class/cmsis_dap_v2.rs @@ -61,10 +61,10 @@ impl<'d, D: Driver<'d>> CmsisDapV2Class<'d, D> { )); let mut interface = function.interface(); let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string)); - let read_ep = alt.endpoint_bulk_out(max_packet_size); - let write_ep = alt.endpoint_bulk_in(max_packet_size); + let read_ep = alt.endpoint_bulk_out(None, max_packet_size); + let write_ep = alt.endpoint_bulk_in(None, max_packet_size); let trace_ep = if trace { - Some(alt.endpoint_bulk_in(max_packet_size)) + Some(alt.endpoint_bulk_in(None, max_packet_size)) } else { None }; diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 6d9e0aced..182e1f83f 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -133,9 +133,9 @@ fn build<'d, D: Driver<'d>>( ], ); - let ep_in = alt.endpoint_interrupt_in(config.max_packet_size, config.poll_ms); + let ep_in = alt.endpoint_interrupt_in(None, config.max_packet_size, config.poll_ms); let ep_out = if with_out_endpoint { - Some(alt.endpoint_interrupt_out(config.max_packet_size, config.poll_ms)) + Some(alt.endpoint_interrupt_out(None, config.max_packet_size, config.poll_ms)) } else { None }; diff --git a/embassy-usb/src/class/midi.rs b/embassy-usb/src/class/midi.rs index 52a96f278..1d152ca44 100644 --- a/embassy-usb/src/class/midi.rs +++ b/embassy-usb/src/class/midi.rs @@ -129,14 +129,14 @@ impl<'d, D: Driver<'d>> MidiClass<'d, D> { for i in 0..n_out_jacks { endpoint_data[2 + i as usize] = in_jack_id_emb(i); } - let read_ep = alt.endpoint_bulk_out(max_packet_size); + let read_ep = alt.endpoint_bulk_out(None, max_packet_size); alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]); endpoint_data[1] = n_in_jacks; for i in 0..n_in_jacks { endpoint_data[2 + i as usize] = out_jack_id_emb(i); } - let write_ep = alt.endpoint_bulk_in(max_packet_size); + let write_ep = alt.endpoint_bulk_in(None, max_packet_size); alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]); MidiClass { read_ep, write_ep } diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs index 1ff29088c..9565e2a25 100644 --- a/embassy-usb/src/class/uac1/speaker.rs +++ b/embassy-usb/src/class/uac1/speaker.rs @@ -268,9 +268,10 @@ impl<'d, D: Driver<'d>> Speaker<'d, D> { alt.descriptor(CS_INTERFACE, &format_descriptor); - let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1); + let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, None, max_packet_size, 1); let feedback_endpoint = alt.alloc_endpoint_in( EndpointType::Isochronous, + None, 4, // Feedback packets are 24 bit (10.14 format). 1, ); diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index 103269791..0747901d1 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs @@ -96,8 +96,8 @@ async fn main(_spawner: Spawner) { let mut function = builder.function(0xFF, 0, 0); let mut interface = function.interface(); let mut alt = interface.alt_setting(0xFF, 0, 0, None); - let mut read_ep = alt.endpoint_bulk_out(64); - let mut write_ep = alt.endpoint_bulk_in(64); + let mut read_ep = alt.endpoint_bulk_out(None, 64); + let mut write_ep = alt.endpoint_bulk_in(None, 64); drop(function); // Build the builder. diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs index a5dc94d5b..5cecb92f0 100644 --- a/examples/rp/src/bin/usb_webusb.rs +++ b/examples/rp/src/bin/usb_webusb.rs @@ -125,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> { let mut iface = func.interface(); let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); - let write_ep = alt.endpoint_bulk_in(config.max_packet_size); - let read_ep = alt.endpoint_bulk_out(config.max_packet_size); + let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size); + let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size); WebEndpoints { write_ep, read_ep } } diff --git a/examples/rp235x/src/bin/usb_webusb.rs b/examples/rp235x/src/bin/usb_webusb.rs index 75d28c853..a68163b61 100644 --- a/examples/rp235x/src/bin/usb_webusb.rs +++ b/examples/rp235x/src/bin/usb_webusb.rs @@ -125,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> { let mut iface = func.interface(); let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); - let write_ep = alt.endpoint_bulk_in(config.max_packet_size); - let read_ep = alt.endpoint_bulk_out(config.max_packet_size); + let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size); + let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size); WebEndpoints { write_ep, read_ep } } -- cgit From 93e2fdf51267f112adf0c14ccb7b46eb51edd48c Mon Sep 17 00:00:00 2001 From: korbin Date: Sun, 13 Jul 2025 22:44:48 -0600 Subject: consolidate endpoint validation logic in stm32 --- embassy-stm32/src/usb/usb.rs | 78 ++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 05c28aceb..92c1601cc 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -359,6 +359,34 @@ impl<'d, T: Instance> Driver<'d, T> { addr } + fn is_endpoint_available(&self, index: usize, ep_type: EndpointType) -> bool { + if index == 0 && ep_type != EndpointType::Control { + return false; // EP0 is reserved for control + } + + let ep = match self.alloc.get(index) { + Some(ep) => ep, + None => return false, + }; + + let used = ep.used_out || ep.used_in; + + if used && ep.ep_type == EndpointType::Isochronous { + // Isochronous endpoints are always double-buffered. + // Their corresponding endpoint/channel registers are forced to be unidirectional. + // Do not reuse this index. + // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. + return false; + } + + let used_dir = match D::dir() { + Direction::Out => ep.used_out, + Direction::In => ep.used_in, + }; + + !used || (ep.ep_type == ep_type && !used_dir) + } + fn alloc_endpoint( &mut self, ep_type: EndpointType, @@ -376,57 +404,15 @@ impl<'d, T: Instance> Driver<'d, T> { let index = if let Some(addr) = ep_addr { // Use the specified endpoint address - let requested_index = addr.index(); - if requested_index >= EP_COUNT { - return Err(EndpointAllocError); - } - if requested_index == 0 && ep_type != EndpointType::Control { - return Err(EndpointAllocError); // EP0 is reserved for control - } - - let ep = &self.alloc[requested_index]; - let used = ep.used_out || ep.used_in; - if used && (ep.ep_type == EndpointType::Isochronous) { - // Isochronous endpoints are always double-buffered. - // Their corresponding endpoint/channel registers are forced to be unidirectional. - // Do not reuse this index. - return Err(EndpointAllocError); - } - - let used_dir = match D::dir() { - Direction::Out => ep.used_out, - Direction::In => ep.used_in, - }; - if used && (ep.ep_type != ep_type || used_dir) { - return Err(EndpointAllocError); - } - - Some((requested_index, &mut self.alloc[requested_index])) + self.is_endpoint_available::(addr.index(), ep_type) + .then_some(addr.index()) } else { // Find any available endpoint - self.alloc.iter_mut().enumerate().find(|(i, ep)| { - if *i == 0 && ep_type != EndpointType::Control { - return false; // reserved for control pipe - } - let used = ep.used_out || ep.used_in; - if used && (ep.ep_type == EndpointType::Isochronous) { - // Isochronous endpoints are always double-buffered. - // Their corresponding endpoint/channel registers are forced to be unidirectional. - // Do not reuse this index. - // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. - return false; - } - - let used_dir = match D::dir() { - Direction::Out => ep.used_out, - Direction::In => ep.used_in, - }; - !used || (ep.ep_type == ep_type && !used_dir) - }) + (0..self.alloc.len()).find(|&i| self.is_endpoint_available::(i, ep_type)) }; let (index, ep) = match index { - Some(x) => x, + Some(i) => (i, &mut self.alloc[i]), None => return Err(EndpointAllocError), }; -- cgit From 791c32e28408475695f192c4f1bc9a187aaa78e1 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:13:34 +0200 Subject: docs: add missing feature for doc gen --- embassy-net-nrf91/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 62813e546..1c27cabb4 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -33,7 +33,7 @@ at-commands = "0.5.4" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/" target = "thumbv7em-none-eabi" -features = ["defmt"] +features = ["defmt", "nrf-pac/nrf9160"] [package.metadata.docs.rs] features = ["defmt"] -- cgit From 64e1a806fae3a54dcd5009c46e8ab7e66ba1add8 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:46:23 +0200 Subject: chore: update to `embassy-hal-internal` v0.3.0 --- embassy-embedded-hal/Cargo.toml | 2 +- embassy-imxrt/Cargo.toml | 2 +- embassy-mspm0/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-nxp/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index efc3173d4..2bc817758 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -22,7 +22,7 @@ time = ["dep:embassy-time"] default = ["time"] [dependencies] -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index 49dc8089c..a7caa307e 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -67,7 +67,7 @@ embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-time = { version = "0.4", path = "../embassy-time", optional = true } -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index 09bce9aa7..a2ecbc7db 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -31,7 +31,7 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] } embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 47eb92697..71ac70031 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -144,7 +144,7 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 18e9d989c..01f57c4e2 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" cortex-m = "0.7.7" cortex-m-rt = "0.7.0" critical-section = "1.1.2" -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } defmt = { version = "1", optional = true } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 5df9e154c..f09b32a7a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -141,7 +141,7 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } atomic-polyfill = "1.0.1" diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index b749625aa..795c09cb9 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -23,7 +23,7 @@ embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 552113a79..6c1731760 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -53,7 +53,7 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } +embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } -- cgit From 1df59ffec4eed74ade4086da496a32d376e4190a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:40:51 +0200 Subject: chore: update to `embassy-nrf` v0.4.0 --- docs/examples/basic/Cargo.toml | 2 +- embassy-boot-nrf/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index f5cf2b231..09fc517e7 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } -embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } +embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 3cfaa5a80..d15eca0ef 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-nrf = { version = "0.3.1", path = "../embassy-nrf", default-features = false } +embassy-nrf = { version = "0.4.0", path = "../embassy-nrf", default-features = false } embassy-boot = { version = "0.4.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 4d633e8a8..415d013ca 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.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } -embassy-nrf = { version = "0.3.1", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } +embassy-nrf = { version = "0.4.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index d9e8ca2f9..7bf38cb3f 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -18,7 +18,7 @@ log = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } embassy-time = { version = "0.4.0", path = "../../embassy-time" } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 91f78737f..89f78efa0 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 87da89efe..548a16c8d 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index afd269f72..efe57f264 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 4140e49d2..6c741f344 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index dc4fba4fd..0351cfe27 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 4b229d06d..b1189e887 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index a083aa5e7..32501e88d 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index ae98631ef..23238412c 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 25aedf624..f34c3a053 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 30c4223b7..b46498a7a 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -- cgit From a7ffa44a08cfd8daf054c555056ab10360593840 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:49:23 +0200 Subject: chore: Release embassy-hal-internal version 0.3.0 --- embassy-hal-internal/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml index cc360682e..b4c52ccfa 100644 --- a/embassy-hal-internal/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-hal-internal" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY." -- cgit From c7e33b28b8134662a0e0cf3e7215a78a00b2f1dd Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:50:13 +0200 Subject: Revert "chore: update to `embassy-nrf` v0.4.0" This reverts commit 1df59ffec4eed74ade4086da496a32d376e4190a. --- docs/examples/basic/Cargo.toml | 2 +- embassy-boot-nrf/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index 09fc517e7..f5cf2b231 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } -embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } +embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index d15eca0ef..3cfaa5a80 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-nrf = { version = "0.4.0", path = "../embassy-nrf", default-features = false } +embassy-nrf = { version = "0.3.1", path = "../embassy-nrf", default-features = false } embassy-boot = { version = "0.4.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 415d013ca..4d633e8a8 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.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } -embassy-nrf = { version = "0.4.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } +embassy-nrf = { version = "0.3.1", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 7bf38cb3f..d9e8ca2f9 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -18,7 +18,7 @@ log = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } embassy-time = { version = "0.4.0", path = "../../embassy-time" } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 89f78efa0..91f78737f 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 548a16c8d..87da89efe 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index efe57f264..afd269f72 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 6c741f344..4140e49d2 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 0351cfe27..dc4fba4fd 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index b1189e887..4b229d06d 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 32501e88d..a083aa5e7 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 23238412c..ae98631ef 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index f34c3a053..25aedf624 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index b46498a7a..30c4223b7 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -- cgit From 4f50c85221f11eecb334169e98d96b7fb94a2753 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:40:51 +0200 Subject: chore: update to `embassy-nrf` v0.4.0 --- docs/examples/basic/Cargo.toml | 2 +- embassy-boot-nrf/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index f5cf2b231..09fc517e7 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } -embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } +embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 3cfaa5a80..d15eca0ef 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-nrf = { version = "0.3.1", path = "../embassy-nrf", default-features = false } +embassy-nrf = { version = "0.4.0", path = "../embassy-nrf", default-features = false } embassy-boot = { version = "0.4.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 4d633e8a8..415d013ca 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.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } -embassy-nrf = { version = "0.3.1", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } +embassy-nrf = { version = "0.4.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index d9e8ca2f9..7bf38cb3f 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -18,7 +18,7 @@ log = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } embassy-time = { version = "0.4.0", path = "../../embassy-time" } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 91f78737f..89f78efa0 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 87da89efe..548a16c8d 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index afd269f72..efe57f264 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 4140e49d2..6c741f344 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index dc4fba4fd..0351cfe27 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 4b229d06d..b1189e887 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index a083aa5e7..32501e88d 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index ae98631ef..23238412c 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 25aedf624..f34c3a053 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 30c4223b7..b46498a7a 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -- cgit From 7e01c9bb4d8039cbc4b719d8d8f07b9b63b65842 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 11:55:57 +0200 Subject: chore: Release embassy-nrf version 0.4.0 --- embassy-nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 71ac70031..e343cefdb 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-nrf" -version = "0.3.1" +version = "0.4.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" -- cgit From 006f4cdb530b63156e6dd3bdfb094f2065f4c7bb Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 12:00:17 +0200 Subject: chore: add release docs --- RELEASE.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 000000000..711a93ae4 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,59 @@ +# RELEASE.md + +This document outlines the process for releasing Embassy crates using `cargo-release` and the `release/bump-dependency.sh` script. + +When releasing a crate, keep in mind that you may need to recursively release dependencies as well. + +## Prerequisites + +- Install [`cargo-release`](https://github.com/crate-ci/cargo-release): + + ```sh + cargo binstall cargo-release +``` + +## Crate release + +Check if there are changes to the public API since the last release. If there is a breaking change, follow +the process for creating a minor release. Otherewise, follow the process for creating a new patch release. + +Keep in mind that some crates may need the --features and --target flags passed to cargo release. For more information on that, +look at the `Cargo.toml` files. + +### Patch release (no breaking public API changes) + +``` +cd embassy-nrf/ +cargo release patch --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi + +# If dry-run is OK (no missing dependencies on crates.io) +cargo release patch --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi +``` + +### Minor release + +``` +# Bump versions in crate files +./release/bump-dependency.sh embassy-nrf 0.4.0 + +# Commit version bump +git commit -am 'chore: update to `embassy-nrf` v0.4.0' + +# Release crate +cd embassy-nrf/ +cargo release minor --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi + +# If dry-run is OK (no missing dependencies on crates.io) +cargo release minor --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi +``` + +## Push tags + +Push the git tags that `cargo release` created earlier: + +``` +git push --tags +``` +## Reference + +* [PR introducing release automation](https://github.com/embassy-rs/embassy/pull/4289) -- cgit From c62b610db34609c179c55f7bdec368bc8bd4c054 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 12:02:42 +0200 Subject: docs: fix markdown syntax --- RELEASE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 711a93ae4..0ebcfd9fa 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -10,7 +10,6 @@ When releasing a crate, keep in mind that you may need to recursively release de ```sh cargo binstall cargo-release -``` ## Crate release -- cgit From 0c366c21fcb8bc1d04b538cbf852f05bcd9769d9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 12:18:29 +0200 Subject: chore: add missing metadata for changelog generation --- embassy-nrf/CHANGELOG.md | 3 ++- embassy-nrf/release.toml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 embassy-nrf/release.toml diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index ffa7997f7..5863e8f28 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate ## 0.3.1 - 2025-01-09 diff --git a/embassy-nrf/release.toml b/embassy-nrf/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-nrf/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 5a8956209f7626ea8fb2a299a100f268599cad66 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 12:20:38 +0200 Subject: docs: update nrf changelog --- embassy-nrf/CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 5863e8f28..00deb7f20 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- changed: nrf52833: configure internal LDO +- changed: nrf5340: add more options to clock config +- bugfix: clean the SAADC's register while dropping +- changed: Remove Peripheral trait, rename PeripheralRef->Peri. +- changed: take pins before interrupts in buffered uart init +- changed: nrf5340: add wdt support +- changed: remove nrf radio BLE +- changed: add Blocking/Async Mode param. +- bugfix: fix PWM loop count +- bugfix: fixing the nrf54l drive configuration bug +- changed: add temp driver for nrf5340 +- changed: add support for rand 0.9 + ## 0.3.1 - 2025-01-09 - bugfix: nrf twim return errors in async\_wait instead of waiting indefinitely -- cgit From 8e940c315ca2b252229fb88f2266cbe0aea94bd0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 12:21:22 +0200 Subject: chore: Release embassy-nrf version 0.4.1 --- embassy-nrf/CHANGELOG.md | 2 ++ embassy-nrf/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 00deb7f20..30f29d669 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.4.1 - 2025-07-14 + - changed: nrf52833: configure internal LDO - changed: nrf5340: add more options to clock config - bugfix: clean the SAADC's register while dropping diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index e343cefdb..3e43d3c93 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-nrf" -version = "0.4.0" +version = "0.4.1" edition = "2021" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" -- cgit From 416612d2f916d8c49d3a3cb9ffba38ffc2f95c91 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 16:04:32 +0200 Subject: chore: bump embassy-boot-nrf version --- examples/boot/application/nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 415d013ca..e9616aa54 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.4.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } -embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } +embassy-boot-nrf = { version = "0.5.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } -- cgit From f5006284f8defd5134cfd6eec7d144236aeee0c1 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Jul 2025 16:05:11 +0200 Subject: chore: Release embassy-boot-nrf version 0.5.0 --- embassy-boot-nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index d15eca0ef..f5edc0716 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot-nrf" -version = "0.4.0" +version = "0.5.0" description = "Bootloader lib for nRF chips" license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" -- cgit From ed64d99a510dab0d2a8c9a1b6bf362a95e5ffd35 Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Mon, 14 Jul 2025 16:49:50 -0700 Subject: WIP for USB_OTG support on STM32WBA devices --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/usb/mod.rs | 28 ++++++++++++++++++++++++++-- embassy-stm32/src/usb/otg.rs | 21 ++++++++++++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index c086fbbcf..c28636dc8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a3ce0ea3b5bd832ec2ad53465a0d80b0f4e0a" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-60582dd866b34e690f156cd72b91300a9a8057c0" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a3ce0ea3b5bd832ec2ad53465a0d80b0f4e0a", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-60582dd866b34e690f156cd72b91300a9a8057c0", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index ae5963420..692897b59 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -15,7 +15,7 @@ fn common_init() { let freq = T::frequency(); // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally - #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))] + #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs)))] if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { panic!( "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", @@ -25,7 +25,7 @@ fn common_init() { // Check frequency is within the 0.25% tolerance allowed by the spec. // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user // has tight clock restrictions due to something else (like audio). - #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))] + #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] if freq.0.abs_diff(48_000_000) > 120_000 { panic!( "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", @@ -102,6 +102,30 @@ fn common_init() { } } + #[cfg(stm32wba)] + { + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.svmcr().modify(|w| { + w.set_usv(crate::pac::pwr::vals::Usv::B_0X1); + // w.set_uvmen(true); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.vosr().read().vdd11usbrdy() {} + + // Now set up transceiver power if it's a OTG-HS + #[cfg(peri_usb_otg_hs)] + { + crate::pac::PWR.vosr().modify(|w| { + w.set_usbpwren(true); + w.set_usbboosten(true); + }); + while !crate::pac::PWR.vosr().read().usbboostrdy() {} + } + } + T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 590d1a427..d664709d3 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -105,7 +105,7 @@ impl<'d, T: Instance> Driver<'d, T> { config: Config, ) -> Self { // For STM32U5 High speed pins need to be left in analog mode - #[cfg(not(all(stm32u5, peri_usb_otg_hs)))] + #[cfg(not(any(all(stm32u5, peri_usb_otg_hs),all(stm32wba, peri_usb_otg_hs))))] { _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); @@ -323,6 +323,20 @@ impl<'d, T: Instance> Bus<'d, T> { }); } + #[cfg(all(stm32wba, peri_usb_otg_hs))] + { + crate::pac::SYSCFG.otghsphycr().modify(|w| { + w.set_en(true); + }); + + critical_section::with(|_| { + crate::pac::RCC.ahb2enr().modify(|w| { + w.set_otgen(true); + w.set_otghsphyen(true); + }); + }); + } + let r = T::regs(); let core_id = r.cid().read().0; trace!("Core id {:08x}", core_id); @@ -464,6 +478,7 @@ foreach_interrupt!( stm32f7, stm32l4, stm32u5, + stm32wba, ))] { const FIFO_DEPTH_WORDS: u16 = 320; const ENDPOINT_COUNT: usize = 6; @@ -473,7 +488,7 @@ foreach_interrupt!( } else if #[cfg(any(stm32h7, stm32h7rs))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; - } else if #[cfg(stm32u5)] { + } else if #[cfg(any(stm32wba, stm32u5))] { const FIFO_DEPTH_WORDS: u16 = 320; const ENDPOINT_COUNT: usize = 6; } else { @@ -523,7 +538,7 @@ foreach_interrupt!( ))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; - } else if #[cfg(stm32u5)] { + } else if #[cfg(any(stm32wba, stm32u5))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; } else { -- cgit From e2ceb2b1f7cd0fd7778b53aaf8ba1caa71b2f7f5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 11 Jul 2025 17:38:42 +0800 Subject: otg: Improve IN write performance chunks_exact() can be handled by the compiler more efficiently. Previous code was making a memcpy call for each 4 byte chunk slice. Hoisting the fifo out of the loop avoids recalculating the pointer each time. In my benchmark I see a jump from ~13 megabyte/sec to ~25MB/sec after this change (opt-level=3). opt-level = "z" goes 9MB/s to 18MB/s. The benchmark was on a stm32h7s3l8, 600mhz clock, 512 byte bulk writes, data in DTCM. The benchmark isn't just USB writes, also has some unrelated memcpys for packet construction. --- embassy-usb-synopsys-otg/src/lib.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index fc4428b54..3f6531813 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -1210,10 +1210,23 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { }); // Write data to FIFO - for chunk in buf.chunks(4) { + let chunks = buf.chunks_exact(4); + // Stash the last partial chunk + let rem = chunks.remainder(); + let last_chunk = (!rem.is_empty()).then(|| { let mut tmp = [0u8; 4]; - tmp[0..chunk.len()].copy_from_slice(chunk); - self.regs.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); + tmp[0..rem.len()].copy_from_slice(rem); + u32::from_ne_bytes(tmp) + }); + + let fifo = self.regs.fifo(index); + for chunk in chunks { + let val = u32::from_ne_bytes(chunk.try_into().unwrap()); + fifo.write_value(regs::Fifo(val)); + } + // Write any last chunk + if let Some(val) = last_chunk { + fifo.write_value(regs::Fifo(val)); } }); -- cgit From 249433a7ed7a9d29a5da0fe169dc31acaad9b8bb Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 10:25:13 +0200 Subject: chore: update changelog --- cyw43-pio/CHANGELOG.md | 3 ++- cyw43-pio/release.toml | 5 +++++ cyw43/CHANGELOG.md | 7 ++++++- cyw43/release.toml | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 cyw43-pio/release.toml create mode 100644 cyw43/release.toml diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md index 4d56973df..b13f6c75c 100644 --- a/cyw43-pio/CHANGELOG.md +++ b/cyw43-pio/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate - Update embassy-rp to 0.4.0 diff --git a/cyw43-pio/release.toml b/cyw43-pio/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/cyw43-pio/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md index 40a638388..fc4b4930e 100644 --- a/cyw43/CHANGELOG.md +++ b/cyw43/CHANGELOG.md @@ -5,7 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased + +## Unreleased - ReleaseDate + +- bump embassy-sync to 0.7.0 +- bump bt-hci to 0.3.0 +- make State::new const fn ## 0.3.0 - 2025-01-05 diff --git a/cyw43/release.toml b/cyw43/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/cyw43/release.toml @@ -0,0 +1,5 @@ +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## Unreleased - ReleaseDate\n", exactly=1}, +] -- cgit From 9811f1c999521a80cccf82cfe1a5192f9112b5af Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 11:10:56 +0200 Subject: chore: prepare embassy-rp for release --- cyw43-pio/Cargo.toml | 2 +- embassy-boot-rp/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 93a2e7089..7dcbaf00b 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.embassy.dev/cyw43-pio" [dependencies] cyw43 = { version = "0.3.0", path = "../cyw43" } -embassy-rp = { version = "0.4.0", path = "../embassy-rp" } +embassy-rp = { version = "0.5.0", path = "../embassy-rp" } fixed = "1.23.1" defmt = { version = "1.0.1", optional = true } diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index afe5d6691..9b838641b 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-rp = { version = "0.4.0", path = "../embassy-rp", default-features = false } +embassy-rp = { version = "0.5.0", path = "../embassy-rp", default-features = false } embassy-boot = { version = "0.4.0", path = "../embassy-boot" } embassy-time = { version = "0.4.0", path = "../embassy-time" } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index be283fb27..cc0ec0deb 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } -embassy-rp = { version = "0.4.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } +embassy-rp = { version = "0.5.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] } embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 8d05d5a8c..68259f525 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } +embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index c6afe37db..1530d9885 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } +embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 2be37f525..567c2a7b1 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -15,7 +15,7 @@ teleprobe-meta = "1.1" embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", ] } -embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } +embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } -- cgit From e4f500eb1937d1f0d388b09c8f498a26dfc74487 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 11:13:35 +0200 Subject: chore: Release embassy-rp version 0.5.0 --- embassy-rp/CHANGELOG.md | 2 ++ embassy-rp/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 52bf0038e..8989c4371 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.5.0 - 2025-07-15 + - Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975)) - Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012)) - `flash`: rename `BOOTROM_BASE` to `BOOTRAM_BASE` ([#4014](https://github.com/embassy-rs/embassy/pull/4014)) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index f09b32a7a..8b78bb378 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-rp" -version = "0.4.0" +version = "0.5.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" -- cgit From f2413bd20dfc7d96b3615398906a74916b2f2f98 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 11:14:43 +0200 Subject: chore: Release cyw43 version 0.4.0 --- cyw43/CHANGELOG.md | 2 ++ cyw43/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md index fc4b4930e..c800e785b 100644 --- a/cyw43/CHANGELOG.md +++ b/cyw43/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.4.0 - 2025-07-15 + - bump embassy-sync to 0.7.0 - bump bt-hci to 0.3.0 - make State::new const fn diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index c52a653bd..c4b399d29 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyw43" -version = "0.3.0" +version = "0.4.0" edition = "2021" description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] -- cgit From c35d5fb9ce4dc5eaea141f090e8047c738712593 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 11:15:19 +0200 Subject: chore: bump cyw43 version --- cyw43-pio/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 7dcbaf00b..0e7c39223 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/cyw43-pio" [dependencies] -cyw43 = { version = "0.3.0", path = "../cyw43" } +cyw43 = { version = "0.4.0", path = "../cyw43" } embassy-rp = { version = "0.5.0", path = "../embassy-rp" } fixed = "1.23.1" defmt = { version = "1.0.1", optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 68259f525..4469c1267 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -16,7 +16,7 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defm embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } -cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } +cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 1530d9885..c3bc65ba2 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -16,7 +16,7 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defm embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } -cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } +cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" -- cgit From f5b9b83cf4a439607ee0f86e5557b16786c828e1 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 11:15:46 +0200 Subject: chore: bump cyw43-pio dependency --- cyw43-pio/CHANGELOG.md | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md index b13f6c75c..4c3a9c95b 100644 --- a/cyw43-pio/CHANGELOG.md +++ b/cyw43-pio/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate -- Update embassy-rp to 0.4.0 +- Update embassy-rp to 0.5.0 ## 0.3.0 - 2025-01-05 diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 4469c1267..713f5fe05 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -17,7 +17,7 @@ embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", fea embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } +cyw43-pio = { version = "0.5.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index c3bc65ba2..ee37480e4 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -17,7 +17,7 @@ embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", fea embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } +cyw43-pio = { version = "0.5.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" -- cgit From adbe5859c0c3a63afcae93e919109a17af596b6d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 11:17:13 +0200 Subject: chore: Release cyw43-pio version 0.5.0 --- cyw43-pio/CHANGELOG.md | 2 ++ cyw43-pio/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md index 4c3a9c95b..06b18e503 100644 --- a/cyw43-pio/CHANGELOG.md +++ b/cyw43-pio/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.5.0 - 2025-07-15 + - Update embassy-rp to 0.5.0 ## 0.3.0 - 2025-01-05 diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 0e7c39223..7c07d35db 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyw43-pio" -version = "0.4.0" +version = "0.5.0" edition = "2021" description = "RP2040 PIO SPI implementation for cyw43" keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] -- cgit From 27f7cb62362608406af92cef818fce4be6613608 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Jul 2025 16:47:01 +0200 Subject: nrf: add support for nrf9120, nrf9151, nrf9161 approtect. --- embassy-nrf/src/lib.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 9d44ae7e6..44990ed85 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -586,6 +586,8 @@ mod consts { pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32; pub const APPROTECT_ENABLED: u32 = 0x0000_0000; + #[cfg(feature = "_nrf9120")] + pub const APPROTECT_DISABLED: u32 = 0x50FA50FA; } #[cfg(feature = "_nrf5340-app")] @@ -768,6 +770,28 @@ pub fn init(config: config::Config) -> Peripherals { } // nothing to do on the nrf9160, debug is allowed by default. + + // nrf9151, nrf9161 use the new-style approtect that requires writing a register. + #[cfg(feature = "nrf9120-s")] + unsafe { + let p = pac::APPROTECT_S; + + let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + needs_reset |= res == WriteResult::Written; + p.approtect() + .disable() + .write(|w| w.set_disable(pac::approtect::vals::ApprotectDisableDisable::SW_UNPROTECTED)); + + let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED); + needs_reset |= res == WriteResult::Written; + p.secureapprotect() + .disable() + .write(|w| w.set_disable(pac::approtect::vals::SecureapprotectDisableDisable::SW_UNPROTECTED)); + + // TODO: maybe add workaround for this errata + // It uses extra power, not sure how to let the user choose. + // https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev1/page/ERR/nRF9151/Rev1/latest/anomaly_151_36.html#anomaly_151_36 + } } config::Debug::Disallowed => { // TODO: Handle nRF54L @@ -783,6 +807,13 @@ pub fn init(config: config::Config) -> Peripherals { let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); needs_reset |= res == WriteResult::Written; } + + #[cfg(feature = "nrf9120-s")] + { + let p = pac::APPROTECT_S; + p.approtect().forceprotect().write(|w| w.set_forceprotect(true)); + p.secureapprotect().forceprotect().write(|w| w.set_forceprotect(true)); + } } } config::Debug::NotConfigured => {} -- cgit From f32e8f6025ca3787fa402e13cda4031d88e6f518 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 15 Jul 2025 21:40:37 +0200 Subject: release: embassy-usb-driver 0.1.1 --- embassy-nrf/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-usb-driver/CHANGELOG.md | 2 ++ embassy-usb-driver/Cargo.toml | 2 +- embassy-usb-synopsys-otg/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 3e43d3c93..db4ccea04 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -146,7 +146,7 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } -embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 8b78bb378..c4fe0b8bb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -143,7 +143,7 @@ embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } -embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } atomic-polyfill = "1.0.1" defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6c1731760..4ed7b5163 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -56,7 +56,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } -embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md index c02daefdf..512ce407c 100644 --- a/embassy-usb-driver/CHANGELOG.md +++ b/embassy-usb-driver/CHANGELOG.md @@ -8,4 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.1.1 - 2025-07-15 + - Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index e40421649..a148e9ae8 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb-driver" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "MIT OR Apache-2.0" description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices." diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index 6252feaef..304fc0db8 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -19,7 +19,7 @@ target = "thumbv7em-none-eabi" critical-section = "1.1" embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 31fd1c1e0..7ed8f82e9 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -47,7 +47,7 @@ max-handler-count-8 = [] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } -- cgit From 25bae18ea622593b5dca15e8fb96d5e185eb8685 Mon Sep 17 00:00:00 2001 From: Bailey Quarters Date: Tue, 15 Jul 2025 22:04:27 +0200 Subject: RP2350: Correct SRAM8 and SRAM9 base addresses in example memory.x --- examples/rp235x/memory.x | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp235x/memory.x b/examples/rp235x/memory.x index c803896f6..4382e2065 100644 --- a/examples/rp235x/memory.x +++ b/examples/rp235x/memory.x @@ -17,8 +17,8 @@ MEMORY { * of access times. * Example: Separate stacks for core0 and core1. */ - SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K - SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + SRAM8 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM9 : ORIGIN = 0x20081000, LENGTH = 4K } SECTIONS { -- cgit From 31022db25c18111b8e8ab7130d1eec673214f1ec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Jul 2025 00:07:22 +0200 Subject: Disable flaky test --- ci.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci.sh b/ci.sh index 32c9f9321..3dc403171 100755 --- a/ci.sh +++ b/ci.sh @@ -372,6 +372,9 @@ rm out/tests/pimoroni-pico-plus-2/adc # temporarily disabled rm out/tests/pimoroni-pico-plus-2/pwm +# flaky +rm out/tests/rpi-pico/pwm + if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit -- cgit From 38807ff5fd508ce1a1400c1589e029c0361d0ef0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 09:26:46 +0200 Subject: chore: Release embassy-net-driver-channel version 0.3.1 --- cyw43/Cargo.toml | 2 +- embassy-net-adin1110/Cargo.toml | 2 +- embassy-net-driver-channel/CHANGELOG.md | 2 ++ embassy-net-driver-channel/Cargo.toml | 2 +- embassy-net-esp-hosted/Cargo.toml | 2 +- embassy-net-nrf91/Cargo.toml | 2 +- embassy-net-ppp/Cargo.toml | 2 +- embassy-net-wiznet/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- 9 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index c4b399d29..8aef8963c 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -21,7 +21,7 @@ firmware-logs = [] embassy-time = { version = "0.4.0", path = "../embassy-time"} embassy-sync = { version = "0.7.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel"} defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index a620928cb..9cda4dc5b 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml @@ -16,7 +16,7 @@ log = { version = "0.4", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } embedded-hal-bus = { version = "0.1", features = ["async"] } -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } bitfield = "0.14.0" diff --git a/embassy-net-driver-channel/CHANGELOG.md b/embassy-net-driver-channel/CHANGELOG.md index a5c81cf4d..1189e50c3 100644 --- a/embassy-net-driver-channel/CHANGELOG.md +++ b/embassy-net-driver-channel/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.3.1 - 2025-07-16 + - Update `embassy-sync` to v0.7.0 ## 0.3.0 - 2024-08-05 diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index c16c4be74..386d492c6 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-net-driver-channel" -version = "0.3.0" +version = "0.3.1" edition = "2021" license = "MIT OR Apache-2.0" description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index dea74ed65..7ccef84e8 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -20,7 +20,7 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-sync = { version = "0.7.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel"} embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 1c27cabb4..f3c0a0078 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -23,7 +23,7 @@ cortex-m = "0.7.7" embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } heapless = "0.8" embedded-io = "0.6.1" diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml index 0936626eb..b724401c9 100644 --- a/embassy-net-ppp/Cargo.toml +++ b/embassy-net-ppp/Cargo.toml @@ -18,7 +18,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } embedded-io-async = { version = "0.6.1" } -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } ppproto = { version = "0.2.1"} embassy-sync = { version = "0.7.0", path = "../embassy-sync" } diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index a06a09302..3ff01f72b 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-net-wiznet" [dependencies] embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "1.0.1", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 7ed8f82e9..e706bec13 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -49,7 +49,7 @@ max-handler-count-8 = [] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } +embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } defmt = { version = "1", optional = true } log = { version = "0.4.14", optional = true } -- cgit From 386c586afab378584a8622f32bdeb14a6ae60645 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 14:52:21 +0200 Subject: chore: Release embassy-embedded-hal version 0.3.1 --- embassy-boot/Cargo.toml | 2 +- embassy-embedded-hal/CHANGELOG.md | 2 ++ embassy-embedded-hal/Cargo.toml | 2 +- embassy-imxrt/Cargo.toml | 2 +- embassy-mspm0/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/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/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- tests/mspm0/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- 30 files changed, 31 insertions(+), 29 deletions(-) diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index f12e8e304..281278abb 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true } digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embedded-storage = "0.3.1" embedded-storage-async = { version = "0.4.1" } diff --git a/embassy-embedded-hal/CHANGELOG.md b/embassy-embedded-hal/CHANGELOG.md index 6f0655adf..04d95415c 100644 --- a/embassy-embedded-hal/CHANGELOG.md +++ b/embassy-embedded-hal/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.3.1 - 2025-07-16 + - `SpiDevice` cancel safety: always set CS pin to high on drop - Update `embassy-sync` to v0.7.0 diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 2bc817758..aab6e0f1e 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-embedded-hal" -version = "0.3.0" +version = "0.3.1" edition = "2021" license = "MIT OR Apache-2.0" description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy." diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index a7caa307e..df4687043 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -68,7 +68,7 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-time = { version = "0.4", path = "../embassy-time", optional = true } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } defmt = { version = "1.0.1", optional = true } diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index a2ecbc7db..d2adc63d7 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -32,7 +32,7 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } embedded-hal = { version = "1.0" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index db4ccea04..a3feba7d5 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -145,7 +145,7 @@ embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-util embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index c4fe0b8bb..52a69b6ca 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -142,7 +142,7 @@ embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-util embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } atomic-polyfill = "1.0.1" defmt = { version = "1.0.1", optional = true } diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 795c09cb9..a16aa4b54 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -24,7 +24,7 @@ embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } defmt = { version = "1.0.1", optional = true } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4ed7b5163..b19053da6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -54,7 +54,7 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } -embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } +embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index e9616aa54..93fcbfb24 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features embassy-nrf = { version = "0.4.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } embassy-boot-nrf = { version = "0.5.0", path = "../../../../embassy-boot-nrf", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index cc0ec0deb..9571c789d 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.5.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index b3466e288..4cd2d1338 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32" } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 72dbded5f..f3d74f53a 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 57fb8312c..427b15bcb 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 7dbbba138..46e79f7ed 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 9549b2048..ae3cd3600 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 03daeb0bc..a41b25562 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index e582628aa..b0a89c6fe 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb" } embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 3ed04b472..af49db260 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", f embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } -embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } defmt-rtt = { version = "1.0.0", optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 713f5fe05..e52e717b8 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index ee37480e4..3d05eddc2 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 27c59d980..c981e6708 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 8d23c346a..e4938e15b 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 71bd50d60..2ee5bc83f 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h755zi-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 8e960932a..a66e1276d 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 72f86e0cf..7fc13b652 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 7da09e5b0..0c45f9c73 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5ecd77443..1c5d9c07a 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/tests/mspm0/Cargo.toml b/tests/mspm0/Cargo.toml index 5d34ece7e..2d5b8cd52 100644 --- a/tests/mspm0/Cargo.toml +++ b/tests/mspm0/Cargo.toml @@ -15,7 +15,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "d embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt" ] } embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = [ "rt", "defmt", "unstable-pac", "time-driver-any" ] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal/"} +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"} defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 567c2a7b1..86eded861 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -19,7 +19,7 @@ embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = [ "defmt embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } -embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal/"} +embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"} cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } perf-client = { path = "../perf-client" } -- cgit From 24349a90126cb1c082e34b9d8fc14a7c3e08be6a Mon Sep 17 00:00:00 2001 From: Jianqing Liu Date: Sun, 13 Jul 2025 14:34:07 -0400 Subject: Fix CDC ACM BufferedReceiver buffer calculation Co-authored-by: Ralph Ursprung <39383228+rursprung@users.noreply.github.com> --- embassy-usb/src/class/cdc_acm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index a1144ce05..0a1a5e64f 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -501,7 +501,7 @@ impl<'d, D: Driver<'d>> BufferedReceiver<'d, D> { fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize { let available = &self.buffer[self.start..self.end]; let len = core::cmp::min(available.len(), buf.len()); - buf[..len].copy_from_slice(&self.buffer[..len]); + buf[..len].copy_from_slice(&available[..len]); self.start += len; len } -- cgit From c484e7d0e530d20e1fdfaa4a1578e9321cc8b283 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 15:34:59 +0200 Subject: chore: Release embassy-usb version 0.5.0 --- embassy-usb-dfu/Cargo.toml | 2 +- embassy-usb-logger/Cargo.toml | 2 +- embassy-usb/CHANGELOG.md | 3 +++ embassy-usb/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/bootloader/stm32wb-dfu/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h742/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- 29 files changed, 31 insertions(+), 28 deletions(-) diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 8b2467bca..cdad3ce00 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -35,7 +35,7 @@ embassy-boot = { version = "0.4.0", path = "../embassy-boot" } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-time = { version = "0.4.0", path = "../embassy-time" } -embassy-usb = { version = "0.4.0", path = "../embassy-usb", default-features = false } +embassy-usb = { version = "0.5.0", path = "../embassy-usb", default-features = false } embedded-storage = { version = "0.3.1" } esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 6b8e9af47..6dd2637f2 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -15,7 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-l target = "thumbv7em-none-eabi" [dependencies] -embassy-usb = { version = "0.4.0", path = "../embassy-usb" } +embassy-usb = { version = "0.5.0", path = "../embassy-usb" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } log = "0.4" diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 51db5f03e..3ee75e226 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -8,12 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.5.0 - 2025-07-16 + - `UAC1`: unmute by default ([#3992](https://github.com/embassy-rs/embassy/pull/3992)) - `cdc_acm`: `State::new` is now `const` ([#4000](https://github.com/embassy-rs/embassy/pull/4000)) - Add support for CMSIS-DAP v2 USB class ([#4107](https://github.com/embassy-rs/embassy/pull/4107)) - Reduce `UsbDevice` builder logs to `trace` ([#4130](https://github.com/embassy-rs/embassy/pull/4130)) - Implement `embedded-io-async` traits for USB CDC ACM ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) - Update `embassy-sync` to v0.7.0 +- Fix CDC ACM BufferedReceiver buffer calculation ## 0.4.0 - 2025-01-15 diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index e706bec13..1f90f84ad 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb" -version = "0.4.0" +version = "0.5.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Async USB device stack for embedded devices in Rust." diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index b0a89c6fe..287fcf806 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } -embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb" } +embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb" } embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index 0bb93b12e..d101e6ace 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml @@ -18,7 +18,7 @@ embedded-storage = "0.3.1" embedded-storage-async = "0.4.0" cfg-if = "1.0.0" embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] } -embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb", default-features = false } +embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb", default-features = false } embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } [features] diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 6c741f344..bcbb8160c 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -11,7 +11,7 @@ embassy-executor = { version = "0.7.0", path = "../../embassy-executor", feature embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 0351cfe27..03d585ccb 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -11,7 +11,7 @@ embassy-executor = { version = "0.7.0", path = "../../embassy-executor", feature embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } defmt = "1.0.1" diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index e52e717b8..05bc8742e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 3d05eddc2..bf7e6d164 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 261733305..01f4fd84a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index f675b0be1..ab7d6b255 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index b47a81e1b..6595804e1 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index edab9ea00..80c43f2ab 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt" ] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index c5801ea90..5995211d2 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.7.0", path = "../../embassy-executor", feature embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io-async = { version = "0.6.1" } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index bf1e7250e..1c290fcfa 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 3d2c2aa7d..a503420e5 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.8.1" diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index f3fda7ff3..0ac9016d4 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index c981e6708..9053289ea 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -12,7 +12,7 @@ embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32h742/Cargo.toml b/examples/stm32h742/Cargo.toml index 31eff4379..efa71e144 100644 --- a/examples/stm32h742/Cargo.toml +++ b/examples/stm32h742/Cargo.toml @@ -34,7 +34,7 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = [ "medium-ethernet", ] } embedded-io-async = { version = "0.6.1" } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = [ +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = [ "defmt", ] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 2ee5bc83f..eaaeb1ac0 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -12,7 +12,7 @@ embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index a66e1276d..e7fc05948 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -12,7 +12,7 @@ embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 7fc13b652..bfa7f9202 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -11,7 +11,7 @@ embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 5f1ce8dfc..9794c6dbd 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ethernet", "medium-ip", "proto-ipv4"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index a0a7916a7..5d5d401f6 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 0c45f9c73..a6b4efcf8 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 3ea3bcd5c..1379d963c 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.8.1" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 3aa45dc79..612d12ac2 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 777d3ed4c..f92e85852 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "1.0.1" -- cgit From 6f2f469c565f7bacc8e5312f6f2ae20828941308 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 15:41:57 +0200 Subject: chore: Release embassy-usb-driver version 0.2.0 --- embassy-nrf/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-usb-driver/CHANGELOG.md | 4 ++++ embassy-usb-driver/Cargo.toml | 2 +- embassy-usb-synopsys-otg/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index a3feba7d5..bc643942a 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -146,7 +146,7 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } -embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 52a69b6ca..ac3b3fe1a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -143,7 +143,7 @@ embassy-time = { version = "0.4.0", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } -embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } atomic-polyfill = "1.0.1" defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b19053da6..c01859fae 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -56,7 +56,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } -embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md index 512ce407c..15875e087 100644 --- a/embassy-usb-driver/CHANGELOG.md +++ b/embassy-usb-driver/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.2.0 - 2025-07-16 + +- Make USB endpoint allocator methods accept an optional `EndpointAddress`. + ## 0.1.1 - 2025-07-15 - Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index a148e9ae8..de69bf694 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb-driver" -version = "0.1.1" +version = "0.2.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices." diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index 304fc0db8..511cddacf 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -19,7 +19,7 @@ target = "thumbv7em-none-eabi" critical-section = "1.1" embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1f90f84ad..4743fde65 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -47,7 +47,7 @@ max-handler-count-8 = [] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-usb-driver = { version = "0.1.1", path = "../embassy-usb-driver" } +embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } -- cgit From 8c087e3641e2bf1f863c73a388b2f483051e6b29 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 15:47:37 +0200 Subject: chore: release embassy-nrf 0.5.0 and embassy-rp 0.6.0 --- cyw43-pio/Cargo.toml | 2 +- docs/examples/basic/Cargo.toml | 2 +- embassy-boot-nrf/Cargo.toml | 2 +- embassy-boot-rp/Cargo.toml | 2 +- embassy-nrf/CHANGELOG.md | 2 ++ embassy-rp/CHANGELOG.md | 2 ++ examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- 22 files changed, 24 insertions(+), 20 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 7c07d35db..21f365db4 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.embassy.dev/cyw43-pio" [dependencies] cyw43 = { version = "0.4.0", path = "../cyw43" } -embassy-rp = { version = "0.5.0", path = "../embassy-rp" } +embassy-rp = { version = "0.6.0", path = "../embassy-rp" } fixed = "1.23.1" defmt = { version = "1.0.1", optional = true } diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index 09fc517e7..c4b72d81a 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } -embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } +embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index f5edc0716..1230cbd3b 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-nrf = { version = "0.4.0", path = "../embassy-nrf", default-features = false } +embassy-nrf = { version = "0.5.0", path = "../embassy-nrf", default-features = false } embassy-boot = { version = "0.4.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index 9b838641b..23d6c7853 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } -embassy-rp = { version = "0.5.0", path = "../embassy-rp", default-features = false } +embassy-rp = { version = "0.6.0", path = "../embassy-rp", default-features = false } embassy-boot = { version = "0.4.0", path = "../embassy-boot" } embassy-time = { version = "0.4.0", path = "../embassy-time" } diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 30f29d669..48a7b25af 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- changed: update to latest embassy-usb-driver + ## 0.4.1 - 2025-07-14 - changed: nrf52833: configure internal LDO diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 8989c4371..b0dc92b6c 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- update to latest embassy-usb-driver + ## 0.5.0 - 2025-07-15 - Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975)) diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 93fcbfb24..9c7fdf148 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.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } -embassy-nrf = { version = "0.4.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } +embassy-nrf = { version = "0.5.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } embassy-boot-nrf = { version = "0.5.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 9571c789d..afb0871e6 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } -embassy-rp = { version = "0.5.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } +embassy-rp = { version = "0.6.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 7bf38cb3f..f1c40192d 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -18,7 +18,7 @@ log = [ embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } embassy-time = { version = "0.4.0", path = "../../embassy-time" } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 89f78efa0..a21d7f6ce 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 548a16c8d..baa873f22 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index efe57f264..3e499e9bc 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index bcbb8160c..d2baebf8e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 03d585ccb..bdebd5386 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index b1189e887..27d5babee 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 32501e88d..2a492b595 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 23238412c..62ef3e4ce 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index f34c3a053..c896afdbe 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 05bc8742e..c069190fc 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } +embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index bf7e6d164..2efa9dc59 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } +embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index b46498a7a..b167c589e 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.4.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 86eded861..298955fe3 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -15,7 +15,7 @@ teleprobe-meta = "1.1" embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", ] } -embassy-rp = { version = "0.5.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } +embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } -- cgit From 3a7a2720b19dda9449313e04ba68ed4884683add Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 15:48:56 +0200 Subject: chore: Release embassy-rp version 0.6.0 --- embassy-rp/CHANGELOG.md | 2 ++ embassy-rp/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index b0dc92b6c..36e1ea9b4 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.6.0 - 2025-07-16 + - update to latest embassy-usb-driver ## 0.5.0 - 2025-07-15 diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ac3b3fe1a..dcf4e7178 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-rp" -version = "0.5.0" +version = "0.6.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" -- cgit From 6810889ff19c83323ef70ff50171b01f8c54a8f4 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 15:49:34 +0200 Subject: chore: Release embassy-nrf version 0.5.0 --- embassy-nrf/CHANGELOG.md | 2 ++ embassy-nrf/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 48a7b25af..a4cb8ceaf 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.5.0 - 2025-07-16 + - changed: update to latest embassy-usb-driver ## 0.4.1 - 2025-07-14 diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index bc643942a..8fa20580d 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-nrf" -version = "0.4.1" +version = "0.5.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" -- cgit From 1dd8c2a745ff20ec0522d6e1866521cd91b64570 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 16 Jul 2025 15:51:27 +0200 Subject: chore: Release cyw43-pio version 0.5.1 --- cyw43-pio/CHANGELOG.md | 2 ++ cyw43-pio/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md index 06b18e503..218271e15 100644 --- a/cyw43-pio/CHANGELOG.md +++ b/cyw43-pio/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.5.1 - 2025-07-16 + ## 0.5.0 - 2025-07-15 - Update embassy-rp to 0.5.0 diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 21f365db4..d60793bdc 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyw43-pio" -version = "0.5.0" +version = "0.5.1" edition = "2021" description = "RP2040 PIO SPI implementation for cyw43" keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c069190fc..971f99fff 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -17,7 +17,7 @@ embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", fea embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.5.0", path = "../../cyw43-pio", features = ["defmt"] } +cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 2efa9dc59..d909aca1b 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -17,7 +17,7 @@ embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", fea embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.5.0", path = "../../cyw43-pio", features = ["defmt"] } +cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" -- cgit From 0180c8067656f37a601fc6e9672707e46646b86a Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Wed, 16 Jul 2025 16:54:14 +0200 Subject: add `derive(Debug)` for `embassy_rp::i2c::I2c` --- 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 a983b7bc3..172193a07 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -76,6 +76,7 @@ impl Default for Config { pub const FIFO_SIZE: u8 = 16; /// I2C driver. +#[derive(Debug)] pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -- cgit From 62b9b03325ae94f650439070e9838885b7bcb345 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 16 Jul 2025 20:48:44 +0300 Subject: Fix stm32 buffered half-duplex uart receive --- embassy-stm32/src/usart/buffered.rs | 2 ++ embassy-stm32/src/usart/mod.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 73ab46404..729440c46 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -87,6 +87,8 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { r.cr1().modify(|w| { w.set_tcie(false); + // Reenable receiver for half-duplex if it was disabled + w.set_re(true); }); state.tx_done.store(true, Ordering::Release); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8c9028f08..5bece6d66 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -651,7 +651,7 @@ pub fn send_break(regs: &Regs) { /// In case of readback, keep Receiver enabled fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) { let mut cr1 = r.cr1().read(); - if r.cr3().read().hdsel() && !cr1.te() { + if r.cr3().read().hdsel() { cr1.set_te(true); cr1.set_re(enable_readback); r.cr1().write_value(cr1); -- cgit From c78dfa7e31e0808d413ad37de8d966cffe4a8fdd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Jul 2025 20:14:47 +0200 Subject: stm32: fix stm32g0b0 build. It has USB but not HSI48 which would break things. Only g0x1 has HSI48. --- ci.sh | 1 + embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/rcc/mco.rs | 2 +- embassy-stm32/src/usb/otg.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ci.sh b/ci.sh index 3dc403171..05720e4f2 100755 --- a/ci.sh +++ b/ci.sh @@ -155,6 +155,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0b0ce,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 356a2b5bb..eaf8bafbf 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a3ce0ea3b5bd832ec2ad53465a0d80b0f4e0a" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-85e2c0f43f3460b3305a2f97962bd39deed09d13" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a3ce0ea3b5bd832ec2ad53465a0d80b0f4e0a", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-85e2c0f43f3460b3305a2f97962bd39deed09d13", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index c50e071fb..0371b9141 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -74,7 +74,7 @@ macro_rules! impl_peri { }; } -#[cfg(any(rcc_c0, rcc_g0, rcc_u0))] +#[cfg(any(rcc_c0, rcc_g0x0, rcc_g0x1, rcc_u0))] #[allow(unused_imports)] use self::{McoSource as Mco1Source, McoSource as Mco2Source}; diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 3547ded00..e9afc0c54 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -527,7 +527,7 @@ foreach_interrupt!( ))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; - } else if #[cfg(stm32u5)] { + } else if #[cfg(any(stm32u5, stm32wba))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; } else { -- cgit From d3308f7e5a02da5fb6ccdd6059afe8656761a685 Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Wed, 16 Jul 2025 17:32:34 -0700 Subject: Fixed register names from recent stm32-data changes --- embassy-stm32/src/usb/otg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index a8b625e8c..5602a2541 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -335,7 +335,7 @@ impl<'d, T: Instance> Bus<'d, T> { critical_section::with(|_| { crate::pac::RCC.ahb2enr().modify(|w| { - w.set_otgen(true); + w.set_usb_otg_hsen(true); w.set_otghsphyen(true); }); }); -- cgit From f46bfd4c6ff50740b7177c054918da6a393ca7f1 Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Wed, 16 Jul 2025 17:47:48 -0700 Subject: Cargo fmt recent changes --- embassy-stm32/src/usb/otg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 5602a2541..b074cfa1b 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -105,7 +105,7 @@ impl<'d, T: Instance> Driver<'d, T> { config: Config, ) -> Self { // For STM32U5 High speed pins need to be left in analog mode - #[cfg(not(any(all(stm32u5, peri_usb_otg_hs),all(stm32wba, peri_usb_otg_hs))))] + #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] { _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); -- cgit From 17fbfc6ffc21349c2032b6e55200fbc4897606d0 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 17 Jul 2025 13:37:55 +0200 Subject: Removed reference to ADC3 for STM32G4x1 if the peripheral does not exist and added stm32g431kb to CI for testing --- ci.sh | 1 + embassy-stm32/src/adc/g4.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ci.sh b/ci.sh index 05720e4f2..842215a6b 100755 --- a/ci.sh +++ b/ci.sh @@ -161,6 +161,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g431kb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \ diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index cb3e342d8..43498966f 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -545,10 +545,12 @@ mod g4 { const CHANNEL: u8 = 17; } + #[cfg(peri_adc3_common)] impl VrefChannel for crate::peripherals::ADC3 { const CHANNEL: u8 = 18; } + #[cfg(peri_adc3_common)] impl VBatChannel for crate::peripherals::ADC3 { const CHANNEL: u8 = 17; } -- cgit From 233bd18fae2da8017a611b9b4f3210af90ff8710 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Thu, 17 Jul 2025 18:00:27 +0200 Subject: STM32F107: Fix inadvertent re-configuration of the SWJ/JTAG pins when activating the (R)MII interface --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/eth/v1/mod.rs | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index eaf8bafbf..0266b53b6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-85e2c0f43f3460b3305a2f97962bd39deed09d13" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f761780b74faf840220b367b15d556b9e10c2334" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-85e2c0f43f3460b3305a2f97962bd39deed09d13", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f761780b74faf840220b367b15d556b9e10c2334", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 01e321bce..b9746231f 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -122,7 +122,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { // Select RMII (Reduced Media Independent Interface) // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); + AFIO.mapr().modify(|w| { + w.set_mii_rmii_sel(true); + w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); + }); RCC.ahbenr().modify(|w| { w.set_ethen(true); @@ -316,7 +319,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { // Select MII (Media Independent Interface) // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| w.set_mii_rmii_sel(false)); + AFIO.mapr().modify(|w| { + w.set_mii_rmii_sel(false); + w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); + }); RCC.ahbenr().modify(|w| { w.set_ethen(true); -- cgit From c279063c426b57f22d8bdeb7356b3c541b16bb08 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sun, 15 Dec 2024 20:15:11 +0100 Subject: STM32F0/F3 Remap DMA channels Fixes #3643 --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/build.rs | 19 ++++++++++++++++++- embassy-stm32/src/macros.rs | 14 +++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b6781905e..c4c2cd013 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) - Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) +- Add automatic setting of remap bits when using alternate DMA channels on STM32F0 and STM32F3 devices ([#3653](https://github.com/embassy-rs/embassy/pull/3653)) ## 0.2.0 - 2025-01-10 diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 753f241c7..068dadf4b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1551,9 +1551,26 @@ fn main() { quote!(()) }; + let mut remap = quote!(); + for remap_info in ch.remap { + let peripheral = format_ident!("{}", remap_info.peripheral); + let register = format_ident!("{}", remap_info.register.to_lowercase()); + let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); + + let value = if remap_info.value.contains("true") || remap_info.value.contains("false") { + let value = format_ident!("{}", remap_info.value); + quote!(#value) + } else { + let value = remap_info.value.parse::().unwrap(); + quote!(#value.into()) + }; + + remap.extend(quote!(crate::pac::#peripheral.#register().modify(|w| w.#setter(#value));)); + } + let channel = format_ident!("{}", channel); g.extend(quote! { - dma_trait_impl!(#tr, #peri, #channel, #request); + dma_trait_impl!(#tr, #peri, #channel, #request, {#remap}); }); } } diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 7526bb180..3a0b490ba 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -81,17 +81,28 @@ macro_rules! dma_trait { /// Note: in some chips, ST calls this the "channel", and calls channels "streams". /// `embassy-stm32` always uses the "channel" and "request number" names. fn request(&self) -> crate::dma::Request; + #[doc = "Remap the DMA channel"] + fn remap(&self); } }; } #[allow(unused)] macro_rules! dma_trait_impl { - (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr) => { + (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr, $remap:expr) => { impl crate::$mod::$trait for crate::peripherals::$channel { fn request(&self) -> crate::dma::Request { $request } + + fn remap(&self) { + critical_section::with(|_| { + #[allow(unused_unsafe)] + unsafe { + $remap; + } + }); + } } }; } @@ -111,6 +122,7 @@ macro_rules! new_dma_nonopt { macro_rules! new_dma { ($name:ident) => {{ let dma = $name; + dma.remap(); let request = dma.request(); Some(crate::dma::ChannelAndRequest { channel: dma.into(), -- cgit From d9f0d80f733abba5d1ca9e1f8b0b81582975d9a1 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Tue, 15 Jul 2025 09:45:05 +0200 Subject: Make remap value a number --- embassy-stm32/build.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 068dadf4b..11b3b4479 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1557,11 +1557,21 @@ fn main() { let register = format_ident!("{}", remap_info.register.to_lowercase()); let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); - let value = if remap_info.value.contains("true") || remap_info.value.contains("false") { - let value = format_ident!("{}", remap_info.value); - quote!(#value) + let field_metadata = METADATA + .peripherals + .iter() + .filter(|p| p.name.eq_ignore_ascii_case(remap_info.peripheral)) + .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter()) + .filter(|f| f.name.eq_ignore_ascii_case(remap_info.register)) + .flat_map(|f| f.fields.iter()) + .find(|f| f.name.eq_ignore_ascii_case(remap_info.field)) + .unwrap(); + + let value = if field_metadata.bit_size == 1 { + let bool_value = format_ident!("{}", remap_info.value > 0); + quote!(#bool_value) } else { - let value = remap_info.value.parse::().unwrap(); + let value = remap_info.value; quote!(#value.into()) }; -- cgit From 5ef796ded0aabfb74e25d9050476f9ac9cd2c6ac Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Thu, 17 Jul 2025 17:23:02 +0200 Subject: Refactor --- embassy-stm32/build.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 11b3b4479..ad07b0269 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1553,14 +1553,13 @@ fn main() { let mut remap = quote!(); for remap_info in ch.remap { - let peripheral = format_ident!("{}", remap_info.peripheral); let register = format_ident!("{}", remap_info.register.to_lowercase()); let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); let field_metadata = METADATA .peripherals .iter() - .filter(|p| p.name.eq_ignore_ascii_case(remap_info.peripheral)) + .filter(|p| p.name == "SYSCFG") .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter()) .filter(|f| f.name.eq_ignore_ascii_case(remap_info.register)) .flat_map(|f| f.fields.iter()) @@ -1575,7 +1574,7 @@ fn main() { quote!(#value.into()) }; - remap.extend(quote!(crate::pac::#peripheral.#register().modify(|w| w.#setter(#value));)); + remap.extend(quote!(crate::pac::SYSCFG.#register().modify(|w| w.#setter(#value));)); } let channel = format_ident!("{}", channel); -- cgit From a3c367d54ef6ec0fe7a2c358c209871a486eadcb Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Thu, 17 Jul 2025 21:08:33 +0200 Subject: Update stm32-data-generated dependency --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0266b53b6..d9c5d1dd9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f761780b74faf840220b367b15d556b9e10c2334" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6e47f105286c0de07f641e22f27db060f1395e86" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f761780b74faf840220b367b15d556b9e10c2334", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6e47f105286c0de07f641e22f27db060f1395e86", default-features = false, features = ["metadata"] } [features] default = ["rt"] -- cgit From 7be5ce2a31cc2106d589dbb63552ebc509eb27bb Mon Sep 17 00:00:00 2001 From: Bailey Quarters Date: Thu, 17 Jul 2025 23:16:05 +0200 Subject: RP2350: Fix PIO clock divider in the blinky Wi-Fi example --- examples/rp235x/src/bin/blinky_wifi.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/rp235x/src/bin/blinky_wifi.rs b/examples/rp235x/src/bin/blinky_wifi.rs index 8c352ebc4..ef6057a1c 100644 --- a/examples/rp235x/src/bin/blinky_wifi.rs +++ b/examples/rp235x/src/bin/blinky_wifi.rs @@ -5,7 +5,7 @@ #![no_std] #![no_main] -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER}; use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; @@ -58,7 +58,9 @@ async fn main(spawner: Spawner) { let spi = PioSpi::new( &mut pio.common, pio.sm0, - DEFAULT_CLOCK_DIVIDER, + // SPI communication won't work if the speed is too high, so we use a divider larger than `DEFAULT_CLOCK_DIVIDER`. + // See: https://github.com/embassy-rs/embassy/issues/3960. + RM2_CLOCK_DIVIDER, pio.irq0, cs, p.PIN_24, -- cgit From 6d79c4c81187d5f2704e2d2f72a3deba05ca449a Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 2 Jul 2025 15:24:01 +0300 Subject: feat: added log-to-defmt feature Signed-off-by: Roy --- ci.sh | 1 + embassy-nxp/Cargo.toml | 4 ++++ embassy-nxp/src/lib.rs | 5 +++-- examples/lpc55s69/Cargo.toml | 4 ++++ examples/lpc55s69/src/bin/log_to_defmt.rs | 18 ++++++++++++++++++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 examples/lpc55s69/src/bin/log_to_defmt.rs diff --git a/ci.sh b/ci.sh index 842215a6b..4f8845158 100755 --- a/ci.sh +++ b/ci.sh @@ -264,6 +264,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ + --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nxp/log-to-defmt --artifact-dir out/examples/lpc55s69 \ --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 01f57c4e2..9eb48be17 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -10,6 +10,8 @@ critical-section = "1.1.2" embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } defmt = { version = "1", optional = true } +log = "0.4.27" +log-to-defmt = { version = "0.1.0", optional = true} ## Chip dependencies lpc55-pac = { version = "0.5.0", optional = true } @@ -30,3 +32,5 @@ unstable-pac = [] #! ### Chip selection features lpc55 = ["lpc55-pac"] +## Enable debug logs +log-to-defmt = ["dep:log-to-defmt"] diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 433aca9e0..79c66e7f6 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -3,7 +3,6 @@ pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; - // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] mod chip; @@ -25,8 +24,10 @@ pub fn init(_config: config::Config) -> Peripherals { { gpio::init(); pint::init(); + #[cfg(feature = "log-to-defmt")] + log_to_defmt::setup(); + log::info!("Initialization complete"); } - crate::Peripherals::take() } diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 6ec6e51a8..7e3b82432 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -18,5 +18,9 @@ defmt-rtt = "1.0.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } panic-semihosting = "0.6.0" +[features] +## To test all-logs mode +log-to-defmt = ["embassy-nxp/log-to-defmt"] + [profile.release] debug = 2 diff --git a/examples/lpc55s69/src/bin/log_to_defmt.rs b/examples/lpc55s69/src/bin/log_to_defmt.rs new file mode 100644 index 000000000..7aaab5e54 --- /dev/null +++ b/examples/lpc55s69/src/bin/log_to_defmt.rs @@ -0,0 +1,18 @@ +/// To test log-to-defmt feature, you have to run the binary file with the corresponding flag +/// Example: cargo run --bin --feature log-to-defmt + + +#![no_std] +#![no_main] + +use log::*; +use embassy_executor::Spawner; +use {defmt_rtt as _, panic_halt as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World"); + loop{ + info!("Another test"); + } +} -- cgit From 3e2b23d2f453d10324896484f9d045d2821bd567 Mon Sep 17 00:00:00 2001 From: Nils Ponsard Date: Fri, 18 Jul 2025 16:26:17 +0200 Subject: feat(embassy-nrf): add uicr hfxo workaround In the MDK, there is a workaround "UICR_HFXO_WORKAROUND" that resests the HFXO values if they are erased. This is necessary on my nrf9151 to have the modem working properly. I found this thanks to these two messages: https://github.com/diondokter/nrf-modem/issues/32#issuecomment-2704598018 https://devzone.nordicsemi.com/f/nordic-q-a/96093/nrf9160-porting-the-modem-library-to-work-with-bare-metal-application/435351?focus=true Signed-off-by: Nils Ponsard --- embassy-nrf/src/lib.rs | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 44990ed85..8a88051b4 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -584,6 +584,8 @@ pub mod config { #[allow(unused)] mod consts { pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; + pub const UICR_HFXOSRC: *mut u32 = 0x00FF801C as *mut u32; + pub const UICR_HFXOCNT: *mut u32 = 0x00FF8020 as *mut u32; pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32; pub const APPROTECT_ENABLED: u32 = 0x0000_0000; #[cfg(feature = "_nrf9120")] @@ -650,13 +652,18 @@ unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteRe return WriteResult::Failed; } - let nvmc = pac::NVMC; - nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN)); - while !nvmc.ready().read().ready() {} - address.write_volatile(value | !mask); - while !nvmc.ready().read().ready() {} - nvmc.config().write(|_| {}); - while !nvmc.ready().read().ready() {} + // Nrf9151 errata 7, need to disable interrups + use DSB https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev2/page/ERR/nRF9151/Rev2/latest/anomaly_151_7.html + cortex_m::interrupt::free(|_cs| { + let nvmc = pac::NVMC; + + nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN)); + while !nvmc.ready().read().ready() {} + address.write_volatile(value | !mask); + cortex_m::asm::dsb(); + while !nvmc.ready().read().ready() {} + nvmc.config().write(|_| {}); + while !nvmc.ready().read().ready() {} + }); WriteResult::Written } @@ -674,6 +681,28 @@ pub fn init(config: config::Config) -> Peripherals { #[allow(unused_mut)] let mut needs_reset = false; + // Workaround used in the nrf mdk: file system_nrf91.c , function SystemInit(), after `#if !defined(NRF_SKIP_UICR_HFXO_WORKAROUND)` + #[cfg(all(feature = "_nrf91", feature = "_s"))] + { + let uicr = pac::UICR_S; + let hfxocnt = uicr.hfxocnt().read().hfxocnt().to_bits(); + let hfxosrc = uicr.hfxosrc().read().hfxosrc().to_bits(); + + if hfxosrc == 1 { + unsafe { + let _ = uicr_write(consts::UICR_HFXOSRC, 0); + } + needs_reset = true; + } + + if hfxocnt == 255 { + unsafe { + let _ = uicr_write(consts::UICR_HFXOCNT, 32); + } + needs_reset = true; + } + } + // Setup debug protection. #[cfg(not(feature = "_nrf51"))] match config.debug { -- cgit From d9cd93ca228b7d4d38066d88b50ff06bb7a5297a Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Fri, 18 Jul 2025 14:23:52 -0700 Subject: Added RTC low-power support for STM32WBA65 Also added low-power feature for STM32WBA65RI build --- ci.sh | 2 +- embassy-stm32/src/rtc/low_power.rs | 12 +++++++++--- embassy-stm32/src/rtc/mod.rs | 2 +- embassy-stm32/src/rtc/v3.rs | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ci.sh b/ci.sh index 842215a6b..229ddaae8 100755 --- a/ci.sh +++ b/ci.sh @@ -172,7 +172,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,low-power,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index cd075f3de..78ccd3e6c 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -66,7 +66,7 @@ pub(crate) enum WakeupPrescaler { } #[cfg(any( - stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 + stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba ))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { @@ -82,7 +82,7 @@ impl From for crate::pac::rtc::vals::Wucksel { } #[cfg(any( - stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 + stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba ))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { @@ -227,7 +227,7 @@ impl Rtc { ::WakeupInterrupt::unpend(); unsafe { ::WakeupInterrupt::enable() }; - #[cfg(not(any(stm32u5, stm32u0)))] + #[cfg(not(any(stm32u5, stm32u0, stm32wba)))] { use crate::pac::EXTI; EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); @@ -247,5 +247,11 @@ impl Rtc { RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true)); } + #[cfg(stm32wba)] + { + use crate::pac::RCC; + // RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); + RCC.apb7smenr().modify(|w| w.set_rtcapbsmen(true)); + } } } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 2c5aaca35..449f3008a 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -296,7 +296,7 @@ trait SealedInstance { const BACKUP_REGISTER_COUNT: usize; #[cfg(feature = "low-power")] - #[cfg(not(any(stm32u5, stm32u0)))] + #[cfg(not(any(stm32wba, stm32u5, stm32u0)))] const EXTI_WAKEUP_LINE: usize; #[cfg(feature = "low-power")] diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 39aa6c5cb..d0b52049e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -146,7 +146,7 @@ impl SealedInstance for crate::peripherals::RTC { type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; } else if #[cfg(any(stm32g0, stm32u0))] { type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; - } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { + } else if #[cfg(any(stm32l5, stm32h5, stm32u5, stm32wba))] { type WakeupInterrupt = crate::interrupt::typelevel::RTC; } ); -- cgit From 8c696a579f763163c8820904ba7e3f4d3eaba2a5 Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Sat, 19 Jul 2025 21:41:03 -0700 Subject: embassy-stm32: account for WBA devices and VDDIO2 - Different power domain - Same split domain as STM32U5 - Added low_power fixes for STM32WBA too --- embassy-stm32/src/lib.rs | 13 ++++++++++--- embassy-stm32/src/low_power.rs | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 06c91ef97..700905850 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -242,12 +242,12 @@ pub struct Config { #[cfg(dbgmcu)] pub enable_debug_during_sleep: bool, - /// On low-power boards (eg. `stm32l4`, `stm32l5` and `stm32u5`), + /// On low-power boards (eg. `stm32l4`, `stm32l5`, `stm32wba` and `stm32u5`), /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`), /// which needs to be enabled before these pins can be used. /// /// May increase power consumption. Defaults to true. - #[cfg(any(stm32l4, stm32l5, stm32u5))] + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] pub enable_independent_io_supply: bool, /// On the U5 series all analog peripherals are powered by a separate supply. @@ -291,7 +291,7 @@ impl Default for Config { rcc: Default::default(), #[cfg(dbgmcu)] enable_debug_during_sleep: true, - #[cfg(any(stm32l4, stm32l5, stm32u5))] + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] enable_independent_io_supply: true, #[cfg(stm32u5)] enable_independent_analog_supply: true, @@ -540,6 +540,13 @@ fn init_hw(config: Config) -> Peripherals { w.set_iosv(config.enable_independent_io_supply); }); } + #[cfg(stm32wba)] + { + use crate::pac::pwr::vals; + crate::pac::PWR.svmcr().modify(|w| { + w.set_io2sv(vals::Io2sv::B_0X1); + }); + } #[cfg(stm32u5)] { crate::pac::PWR.svmcr().modify(|w| { diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 7734365f1..4607eb230 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -124,10 +124,10 @@ pub enum StopMode { Stop2, } -#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] use stm32_metapac::pwr::vals::Lpms; -#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] impl Into for StopMode { fn into(self) -> Lpms { match self { @@ -198,7 +198,7 @@ impl Executor { #[allow(unused_variables)] fn configure_stop(&mut self, stop_mode: StopMode) { - #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] crate::pac::PWR.pmcr().modify(|v| { -- cgit From 2be8be074764f292822ddf022cc81a5d441ad28d Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 20 Jul 2025 18:45:48 +0100 Subject: Use `unsafe` block in IRQ handlers --- embassy-imxrt/src/lib.rs | 8 +++++--- embassy-mspm0/src/lib.rs | 10 ++++++---- embassy-nrf/src/lib.rs | 10 ++++++---- embassy-rp/src/lib.rs | 10 ++++++---- embassy-stm32/src/lib.rs | 10 ++++++---- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index 5846afe5c..a3437c655 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -76,9 +76,11 @@ macro_rules! bind_interrupts { #[allow(non_snake_case)] #[no_mangle] unsafe extern "C" fn $irq() { - $( - <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); - )* + unsafe { + $( + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + )* + } } $( diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index bb8d91403..629ebfa1f 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -111,11 +111,13 @@ macro_rules! bind_interrupts { #[no_mangle] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { - $( - $(#[cfg($cond_handler)])? - <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + unsafe { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); - )* + )* + } } $(#[cfg($cond_irq)])? diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 44990ed85..caa274ff5 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -240,11 +240,13 @@ macro_rules! bind_interrupts { #[no_mangle] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { - $( - $(#[cfg($cond_handler)])? - <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + unsafe { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); - )* + )* + } } $(#[cfg($cond_irq)])? diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index f3c5a35bb..9c450b6dc 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -189,11 +189,13 @@ macro_rules! bind_interrupts { #[no_mangle] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { - $( - $(#[cfg($cond_handler)])? - <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + unsafe { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); - )* + )* + } } $(#[cfg($cond_irq)])? diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 06c91ef97..46661008f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -196,11 +196,13 @@ macro_rules! bind_interrupts { $(#[cfg($cond_irq)])? $(#[doc = $doc])* unsafe extern "C" fn $irq() { - $( - $(#[cfg($cond_handler)])? - <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + unsafe { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); - )* + )* + } } $(#[cfg($cond_irq)])? -- cgit From e1407f80c00f3749235c0827c14a50a64d87dfb7 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sat, 19 Jul 2025 21:23:40 +0100 Subject: Control RFWKPSEL with ClockMux --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/build.rs | 7 +++++++ embassy-stm32/src/ipcc.rs | 3 --- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d9c5d1dd9..1a73d84b6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6e47f105286c0de07f641e22f27db060f1395e86" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-dded8a33a460ae0eb182aee3ccb048beb659982b" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6e47f105286c0de07f641e22f27db060f1395e86", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-dded8a33a460ae0eb182aee3ccb048beb659982b", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ad07b0269..f4781380c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -505,6 +505,13 @@ fn main() { field: "CLK48SEL", }, ); + clock_gen.chained_muxes.insert( + "RFWKP", + &PeripheralRccRegister { + register: "CSR", + field: "RFWKPSEL", + }, + ); } if chip_name.starts_with("stm32f7") { clock_gen.chained_muxes.insert( diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 20cd20dca..670d8332c 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -104,9 +104,6 @@ impl Ipcc { rcc::enable_and_reset::(); IPCC::set_cpu2(true); - // set RF wake-up clock = LSE - crate::pac::RCC.csr().modify(|w| w.set_rfwkpsel(0b01)); - let regs = IPCC::regs(); regs.cpu(0).cr().modify(|w| { -- cgit From 876ad001128823e8069625c6ad7428c0fdabf7a8 Mon Sep 17 00:00:00 2001 From: obe1line Date: Mon, 21 Jul 2025 12:52:23 +1000 Subject: Update Cargo.toml Added STMC071 and other missing C0 chips --- embassy-stm32/Cargo.toml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1a73d84b6..248639385 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -237,6 +237,47 @@ stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] +stm32c051c6 = [ "stm32-metapac/stm32c051c6" ] +stm32c051c8 = [ "stm32-metapac/stm32c051c8" ] +stm32c051d8 = [ "stm32-metapac/stm32c051d8" ] +stm32c051f6 = [ "stm32-metapac/stm32c051f6" ] +stm32c051f8 = [ "stm32-metapac/stm32c051f8" ] +stm32c051g6 = [ "stm32-metapac/stm32c051g6" ] +stm32c051g8 = [ "stm32-metapac/stm32c051g8" ] +stm32c051k6 = [ "stm32-metapac/stm32c051k6" ] +stm32c051k8 = [ "stm32-metapac/stm32c051k8" ] +stm32c071c8 = [ "stm32-metapac/stm32c071c8" ] +stm32c071cb = [ "stm32-metapac/stm32c071cb" ] +stm32c071f8 = [ "stm32-metapac/stm32c071f8" ] +stm32c071fb = [ "stm32-metapac/stm32c071fb" ] +stm32c071g8 = [ "stm32-metapac/stm32c071g8" ] +stm32c071gb = [ "stm32-metapac/stm32c071gb" ] +stm32c071k8 = [ "stm32-metapac/stm32c071k8" ] +stm32c071kb = [ "stm32-metapac/stm32c071kb" ] +stm32c071r8 = [ "stm32-metapac/stm32c071r8" ] +stm32c071rb = [ "stm32-metapac/stm32c071rb" ] +stm32c091cb = [ "stm32-metapac/stm32c091cb" ] +stm32c091cc = [ "stm32-metapac/stm32c091cc" ] +stm32c091ec = [ "stm32-metapac/stm32c091ec" ] +stm32c091fb = [ "stm32-metapac/stm32c091fb" ] +stm32c091fc = [ "stm32-metapac/stm32c091fc" ] +stm32c091gb = [ "stm32-metapac/stm32c091gb" ] +stm32c091gc = [ "stm32-metapac/stm32c091gc" ] +stm32c091kb = [ "stm32-metapac/stm32c091kb" ] +stm32c091kc = [ "stm32-metapac/stm32c091kc" ] +stm32c091rb = [ "stm32-metapac/stm32c091rb" ] +stm32c091rc = [ "stm32-metapac/stm32c091rc" ] +stm32c092cb = [ "stm32-metapac/stm32c092cb" ] +stm32c092cc = [ "stm32-metapac/stm32c092cc" ] +stm32c092ec = [ "stm32-metapac/stm32c092ec" ] +stm32c092fb = [ "stm32-metapac/stm32c092fb" ] +stm32c092fc = [ "stm32-metapac/stm32c092fc" ] +stm32c092gb = [ "stm32-metapac/stm32c092gb" ] +stm32c092gc = [ "stm32-metapac/stm32c092gc" ] +stm32c092kb = [ "stm32-metapac/stm32c092kb" ] +stm32c092kc = [ "stm32-metapac/stm32c092kc" ] +stm32c092rb = [ "stm32-metapac/stm32c092rb" ] +stm32c092rc = [ "stm32-metapac/stm32c092rc" ] stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] stm32f030cc = [ "stm32-metapac/stm32f030cc" ] -- cgit From 87f469792a5e41cb415ce51dd6fa85908248b478 Mon Sep 17 00:00:00 2001 From: obe1line Date: Mon, 21 Jul 2025 13:37:48 +1000 Subject: Added rcc_c0v2 to fix undefined McoSource with STM32C071 --- embassy-stm32/src/rcc/mco.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 0371b9141..96e628b1a 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -74,7 +74,7 @@ macro_rules! impl_peri { }; } -#[cfg(any(rcc_c0, rcc_g0x0, rcc_g0x1, rcc_u0))] +#[cfg(any(rcc_c0, rcc_c0v2, rcc_g0x0, rcc_g0x1, rcc_u0))] #[allow(unused_imports)] use self::{McoSource as Mco1Source, McoSource as Mco2Source}; -- cgit From 6bfdbf0ed827da7ab447359f97799cc1b707fde9 Mon Sep 17 00:00:00 2001 From: obe1line Date: Mon, 21 Jul 2025 14:27:05 +1000 Subject: Added stm32fc071 to cfg to force RCC.cr to be used --- embassy-stm32/src/rcc/hsi48.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs index 3ea5c96c9..49be4af5e 100644 --- a/embassy-stm32/src/rcc/hsi48.rs +++ b/embassy-stm32/src/rcc/hsi48.rs @@ -39,9 +39,9 @@ pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz { }); // Enable HSI48 - #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0)))] + #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0, stm32c071)))] let r = RCC.crrcr(); - #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba))] + #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32c071))] let r = RCC.cr(); #[cfg(any(stm32f0))] let r = RCC.cr2(); -- cgit From ac996e7e0a08f0a8914c76e4ee040e75a4b2b19b Mon Sep 17 00:00:00 2001 From: obe1line Date: Mon, 21 Jul 2025 14:31:48 +1000 Subject: Added ccipr1 conditional for STM32C071 --- embassy-stm32/src/rcc/c0.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index cac2a9149..5a584d993 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -192,8 +192,12 @@ pub(crate) unsafe fn init(config: Config) { lse: None, ); - RCC.ccipr() - .modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); + #[cfg(not(any(stm32c071)))] + let r = RCC.ccipr(); + #[cfg(any(stm32c071))] + let r = RCC.ccipr1(); + + r.modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); } mod max { -- cgit From 26232778e69bdfddcd1df0747b7414ef936e3ea2 Mon Sep 17 00:00:00 2001 From: obe1line Date: Mon, 21 Jul 2025 14:39:07 +1000 Subject: hsi48 field missing for STM32C071 --- embassy-stm32/src/rcc/c0.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 5a584d993..d44914719 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -186,7 +186,10 @@ pub(crate) unsafe fn init(config: Config) { hsiker: hsiker, hse: hse, rtc: rtc, - + + #[cfg(any(stm32c071))] + hsi48: hsi, + // TODO lsi: None, lse: None, -- cgit From 0fc1ab290fed27301b455a039c2ae9c16ce7c30c Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 21 Jul 2025 10:47:21 +0300 Subject: Revert "feat: added log-to-defmt feature" This reverts commit 6d79c4c81187d5f2704e2d2f72a3deba05ca449a. --- ci.sh | 1 - embassy-nxp/Cargo.toml | 4 ---- embassy-nxp/src/lib.rs | 5 ++--- examples/lpc55s69/Cargo.toml | 4 ---- examples/lpc55s69/src/bin/log_to_defmt.rs | 18 ------------------ 5 files changed, 2 insertions(+), 30 deletions(-) delete mode 100644 examples/lpc55s69/src/bin/log_to_defmt.rs diff --git a/ci.sh b/ci.sh index 4f8845158..842215a6b 100755 --- a/ci.sh +++ b/ci.sh @@ -264,7 +264,6 @@ cargo batch \ --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ - --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nxp/log-to-defmt --artifact-dir out/examples/lpc55s69 \ --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 9eb48be17..01f57c4e2 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -10,8 +10,6 @@ critical-section = "1.1.2" embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } defmt = { version = "1", optional = true } -log = "0.4.27" -log-to-defmt = { version = "0.1.0", optional = true} ## Chip dependencies lpc55-pac = { version = "0.5.0", optional = true } @@ -32,5 +30,3 @@ unstable-pac = [] #! ### Chip selection features lpc55 = ["lpc55-pac"] -## Enable debug logs -log-to-defmt = ["dep:log-to-defmt"] diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 79c66e7f6..433aca9e0 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -3,6 +3,7 @@ pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; + // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] mod chip; @@ -24,10 +25,8 @@ pub fn init(_config: config::Config) -> Peripherals { { gpio::init(); pint::init(); - #[cfg(feature = "log-to-defmt")] - log_to_defmt::setup(); - log::info!("Initialization complete"); } + crate::Peripherals::take() } diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 7e3b82432..6ec6e51a8 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -18,9 +18,5 @@ defmt-rtt = "1.0.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } panic-semihosting = "0.6.0" -[features] -## To test all-logs mode -log-to-defmt = ["embassy-nxp/log-to-defmt"] - [profile.release] debug = 2 diff --git a/examples/lpc55s69/src/bin/log_to_defmt.rs b/examples/lpc55s69/src/bin/log_to_defmt.rs deleted file mode 100644 index 7aaab5e54..000000000 --- a/examples/lpc55s69/src/bin/log_to_defmt.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// To test log-to-defmt feature, you have to run the binary file with the corresponding flag -/// Example: cargo run --bin --feature log-to-defmt - - -#![no_std] -#![no_main] - -use log::*; -use embassy_executor::Spawner; -use {defmt_rtt as _, panic_halt as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - info!("Hello World"); - loop{ - info!("Another test"); - } -} -- cgit From 2a696579275a035ab56023313284f8a66ef37a45 Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 21 Jul 2025 12:49:17 +0300 Subject: feat: fmt.rs was added --- embassy-nxp/Cargo.toml | 2 +- embassy-nxp/src/fmt.rs | 284 ++++++++++++++++++++++++++++++++++++++++++ embassy-nxp/src/gpio/lpc55.rs | 1 + embassy-nxp/src/lib.rs | 1 + embassy-nxp/src/pint.rs | 2 + examples/lpc55s69/Cargo.toml | 2 +- 6 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 embassy-nxp/src/fmt.rs diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 01f57c4e2..56d00bfb2 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -10,6 +10,7 @@ critical-section = "1.1.2" embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } defmt = { version = "1", optional = true } +log = { version = "0.4.27", optional = true } ## Chip dependencies lpc55-pac = { version = "0.5.0", optional = true } @@ -21,7 +22,6 @@ rt = ["lpc55-pac?/rt"] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] - ## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable) unstable-pac = [] # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. diff --git a/embassy-nxp/src/fmt.rs b/embassy-nxp/src/fmt.rs new file mode 100644 index 000000000..27d41ace6 --- /dev/null +++ b/embassy-nxp/src/fmt.rs @@ -0,0 +1,284 @@ +//! Copied from embassy-rp + +#![macro_use] +#![allow(unused)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +#[collapse_debuginfo(yes)] +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! unimplemented { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unimplemented!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unimplemented!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +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 ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +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 ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +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 ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +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 ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +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")] +#[collapse_debuginfo(yes)] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +#[collapse_debuginfo(yes)] +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 + } +} + +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 94cd8b7f8..8f407bb3a 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs @@ -7,6 +7,7 @@ pub(crate) fn init() { syscon_reg() .ahbclkctrl0 .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); + info!("GPIO initialized"); } /// The GPIO pin level for pins set on "Digital" mode. diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 433aca9e0..1abaca708 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] +pub mod fmt; pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index dc117e7e3..ff414b4e6 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs @@ -101,6 +101,8 @@ pub(crate) fn init() { crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); }; + + info!("Pin interrupts initialized"); } #[must_use = "futures do nothing unless you `.await` or poll them"] diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 6ec6e51a8..1724a22d4 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] -embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt"] } +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } -- cgit From de4537d000ffeb8821a58056a99e76bb12c1536e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Jul 2025 13:20:35 +0200 Subject: stm32: Fix build for WBA lowpower. --- embassy-stm32/src/low_power.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 4607eb230..d13df5a6b 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -132,7 +132,10 @@ impl Into for StopMode { fn into(self) -> Lpms { match self { StopMode::Stop1 => Lpms::STOP1, + #[cfg(not(stm32wba))] StopMode::Stop2 => Lpms::STOP2, + #[cfg(stm32wba)] + StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? } } } -- cgit From 4c098faef528e18e0b17e540b12487eef9acccbc Mon Sep 17 00:00:00 2001 From: Timo Kröger Date: Mon, 21 Jul 2025 11:26:01 +0200 Subject: chore: Prepare release for `embassy-boot-*` crates Prepare the bootloader crates for a release with `embassy-sync@0.7.0` as dependency for compatiblity with other released crates. `embassy-sync` is used in the public api of `embassy-boot` which is reexported by the `embassy-boot-*` crates. That is why a minor version bump of all crates is required. --- embassy-boot-nrf/Cargo.toml | 4 ++-- embassy-boot-rp/Cargo.toml | 4 ++-- embassy-boot-stm32/Cargo.toml | 4 ++-- embassy-boot/Cargo.toml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 1230cbd3b..81479759c 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot-nrf" -version = "0.5.0" +version = "0.6.0" description = "Bootloader lib for nRF chips" license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" @@ -26,7 +26,7 @@ log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-nrf = { version = "0.5.0", path = "../embassy-nrf", default-features = false } -embassy-boot = { version = "0.4.0", path = "../embassy-boot" } +embassy-boot = { version = "0.5.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.1" diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index 23d6c7853..8ca999f67 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot-rp" -version = "0.5.0" +version = "0.6.0" description = "Bootloader lib for RP2040 chips" license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" @@ -26,7 +26,7 @@ log = { version = "0.4", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-rp = { version = "0.6.0", path = "../embassy-rp", default-features = false } -embassy-boot = { version = "0.4.0", path = "../embassy-boot" } +embassy-boot = { version = "0.5.0", path = "../embassy-boot" } embassy-time = { version = "0.4.0", path = "../embassy-time" } cortex-m = { version = "0.7.6" } diff --git a/embassy-boot-stm32/Cargo.toml b/embassy-boot-stm32/Cargo.toml index 11ad453b8..b92d06c54 100644 --- a/embassy-boot-stm32/Cargo.toml +++ b/embassy-boot-stm32/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot-stm32" -version = "0.3.0" +version = "0.4.0" description = "Bootloader lib for STM32 chips" license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" @@ -26,7 +26,7 @@ log = { version = "0.4", optional = true } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32", default-features = false } -embassy-boot = { version = "0.4.0", path = "../embassy-boot" } +embassy-boot = { version = "0.5.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.1" diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index 281278abb..172330ef2 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot" -version = "0.4.0" +version = "0.5.0" 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" -- cgit From fad100cfa033df7553b0754820ff94ede3376d26 Mon Sep 17 00:00:00 2001 From: Timo Kröger Date: Mon, 21 Jul 2025 13:02:56 +0200 Subject: chore: Update examples to new `nrf-boot-*` version --- examples/boot/application/nrf/Cargo.toml | 4 ++-- examples/boot/application/rp/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/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 9c7fdf148..37183df97 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.5.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } -embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } -embassy-boot-nrf = { version = "0.5.0", path = "../../../../embassy-boot-nrf", features = [] } +embassy-boot = { version = "0.5.0", path = "../../../../embassy-boot", features = [] } +embassy-boot-nrf = { version = "0.6.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index afb0871e6..e5568f6bb 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.6.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } -embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] } +embassy-boot-rp = { version = "0.6.0", path = "../../../../embassy-boot-rp", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = "1.0.1" diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 4cd2d1338..be8b7bff1 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32" } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32" } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index f3d74f53a..2b0175a0c 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 427b15bcb..3c88f4241 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 46e79f7ed..b4e7e090a 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index ae3cd3600..394578e1a 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index a41b25562..abe0451fd 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 287fcf806..bc4681f79 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb" } embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index af49db260..0552d109a 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } +embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } -- cgit From 9b3c97bcf7efc84f1943ee5de2ae251502ca071c Mon Sep 17 00:00:00 2001 From: Timo Kröger Date: Mon, 21 Jul 2025 13:12:48 +0200 Subject: chore: Bump `embassy-boot` version also for `embassy-usb-dfu` --- embassy-usb-dfu/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index cdad3ce00..011046ba4 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -31,7 +31,7 @@ log = { version = "0.4.17", optional = true } bitflags = "2.4.1" cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } -embassy-boot = { version = "0.4.0", path = "../embassy-boot" } +embassy-boot = { version = "0.5.0", path = "../embassy-boot" } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } embassy-sync = { version = "0.7.0", path = "../embassy-sync" } embassy-time = { version = "0.4.0", path = "../embassy-time" } -- cgit From 4db3910011a6a2fddc14f17bcc34a2aeb093d7b6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Jul 2025 14:17:12 +0200 Subject: Disable flaky test rpi-pico/cyw43-perf --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index 229ddaae8..9d3e47a41 100755 --- a/ci.sh +++ b/ci.sh @@ -376,6 +376,7 @@ rm out/tests/pimoroni-pico-plus-2/pwm # flaky rm out/tests/rpi-pico/pwm +rm out/tests/rpi-pico/cyw43-perf if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests -- cgit From 79fbf214ccc7be89d8d656cf0c5b5ffe5425bece Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 20 Jul 2025 15:38:42 +0100 Subject: Enable oversampling for ADC v3 --- embassy-stm32/src/adc/v3.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 313244e19..fd74d5318 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,5 +1,7 @@ use cfg_if::cfg_if; use pac::adc::vals::Dmacfg; +#[cfg(adc_v3)] +use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, @@ -470,6 +472,23 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); } + #[cfg(adc_v3)] + pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + + #[cfg(adc_v3)] + pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(adc_v3)] + pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { cfg_if! { if #[cfg(any(adc_g0, adc_u0))] { -- cgit From f96f68077b4f7d3cb9e9cd914df0d5157c17c297 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Tue, 22 Jul 2025 10:36:29 +0800 Subject: chore: bump embassy-usb-synopsys-otg version Signed-off-by: Haobo Gu --- embassy-stm32/Cargo.toml | 2 +- embassy-usb-synopsys-otg/CHANGELOG.md | 4 ++++ embassy-usb-synopsys-otg/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1a73d84b6..43aee4e1a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", fe embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } -embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } +embassy-usb-synopsys-otg = { version = "0.3.0", path = "../embassy-usb-synopsys-otg" } embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md index 293363d9a..9913ee533 100644 --- a/embassy-usb-synopsys-otg/CHANGELOG.md +++ b/embassy-usb-synopsys-otg/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 0.3.0 - 2025-07-22 + +- Bump `embassy-usb-driver` to v0.2.0 + ## 0.2.0 - 2024-12-06 - Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`) diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index 511cddacf..78cce24de 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb-synopsys-otg" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT OR Apache-2.0" description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers" -- cgit From f3cc62b77d343e0d70f4564ea7f2dadea9cf9d84 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Tue, 22 Jul 2025 14:03:45 +0800 Subject: chore: bump embassy-usb-logger version Signed-off-by: Haobo Gu --- embassy-usb-logger/CHANGELOG.md | 4 ++++ embassy-usb-logger/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-usb-logger/CHANGELOG.md b/embassy-usb-logger/CHANGELOG.md index 86a9fb032..79ea25839 100644 --- a/embassy-usb-logger/CHANGELOG.md +++ b/embassy-usb-logger/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 0.5.0 - 2025-07-22 + +- Update `embassy-usb` to 0.5.0 + ## 0.4.0 - 2025-01-15 - Update `embassy-usb` to 0.4.0 diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 6dd2637f2..68b11ad8a 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb-logger" -version = "0.4.0" +version = "0.5.0" edition = "2021" license = "MIT OR Apache-2.0" description = "`log` implementation for USB serial using `embassy-usb`." diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 971f99fff..eefd69315 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -15,7 +15,7 @@ embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defm embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } +embassy-usb-logger = { version = "0.5.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index d909aca1b..4d3dc77b5 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -15,7 +15,7 @@ embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defm embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } +embassy-usb-logger = { version = "0.5.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] } -- cgit From 1ad5d5a771d5109a763361454fb724b85ae25fdd Mon Sep 17 00:00:00 2001 From: i509VCB Date: Wed, 9 Jul 2025 23:08:59 -0500 Subject: nxp: Add MIMXRT1011 GPIO and time driver PIT is used for the time driver --- .vscode/settings.json | 1 + ci.sh | 2 + embassy-nxp/Cargo.toml | 36 +- embassy-nxp/build.rs | 136 +++++ embassy-nxp/build_common.rs | 94 ++++ embassy-nxp/src/chips/mimxrt1011.rs | 113 +++++ embassy-nxp/src/gpio.rs | 2 + embassy-nxp/src/gpio/rt1xxx.rs | 895 +++++++++++++++++++++++++++++++++ embassy-nxp/src/lib.rs | 87 +++- embassy-nxp/src/time_driver/pit.rs | 187 +++++++ examples/mimxrt1011/.cargo/config.toml | 8 + examples/mimxrt1011/Cargo.toml | 29 ++ examples/mimxrt1011/build.rs | 14 + examples/mimxrt1011/src/bin/blinky.rs | 48 ++ examples/mimxrt1011/src/bin/button.rs | 62 +++ examples/mimxrt1011/src/lib.rs | 75 +++ 16 files changed, 1780 insertions(+), 9 deletions(-) create mode 100644 embassy-nxp/build.rs create mode 100644 embassy-nxp/build_common.rs create mode 100644 embassy-nxp/src/chips/mimxrt1011.rs create mode 100644 embassy-nxp/src/gpio/rt1xxx.rs create mode 100644 embassy-nxp/src/time_driver/pit.rs create mode 100644 examples/mimxrt1011/.cargo/config.toml create mode 100644 examples/mimxrt1011/Cargo.toml create mode 100644 examples/mimxrt1011/build.rs create mode 100644 examples/mimxrt1011/src/bin/blinky.rs create mode 100644 examples/mimxrt1011/src/bin/button.rs create mode 100644 examples/mimxrt1011/src/lib.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index e4814ff27..070e8fbd3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,6 +36,7 @@ // "examples/nrf52840-rtic/Cargo.toml", // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", + // "examples/mimxrt1011/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32c0/Cargo.toml", diff --git a/ci.sh b/ci.sh index 9d3e47a41..e225bc7c9 100755 --- a/ci.sh +++ b/ci.sh @@ -181,6 +181,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ + --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,defmt,time-driver-pit \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ @@ -264,6 +265,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ + --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \ --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 56d00bfb2..625906183 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -11,22 +11,50 @@ embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", fe embassy-sync = { version = "0.7.0", path = "../embassy-sync" } defmt = { version = "1", optional = true } log = { version = "0.4.27", optional = true } +embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } +embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } +embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } ## Chip dependencies lpc55-pac = { version = "0.5.0", optional = true } +nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "1e010dbe75ab0e14dd908e4646391403414c8a8e" } + +imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } + +[build-dependencies] +cfg_aliases = "0.2.1" +nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "1e010dbe75ab0e14dd908e4646391403414c8a8e", features = ["metadata"], optional = true } +proc-macro2 = "1.0.95" +quote = "1.0.15" [features] default = ["rt"] -# Enable PACs as optional dependencies, since some chip families will use different pac crates. -rt = ["lpc55-pac?/rt"] +# Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily). +rt = ["lpc55-pac?/rt", "nxp-pac?/rt"] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] + +log = ["dep:log"] + +## Use Periodic Interrupt Timer (PIT) as the time driver for `embassy-time`, with a tick rate of 1 MHz +time-driver-pit = ["_time_driver", "embassy-time?/tick-hz-1_000_000"] + ## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable) unstable-pac = [] -# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. +# This is unstable because semver-minor (non-breaking) releases of embassy-nxp 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. # There are no plans to make this stable. +## internal use only +# +# This feature is unfortunately a hack around the fact that cfg_aliases cannot apply to the buildscript +# that creates the aliases. +_rt1xxx = [] + +# A timer driver is enabled. +_time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] + #! ### Chip selection features -lpc55 = ["lpc55-pac"] +lpc55 = ["dep:lpc55-pac"] +mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs new file mode 100644 index 000000000..6c10d0e69 --- /dev/null +++ b/embassy-nxp/build.rs @@ -0,0 +1,136 @@ +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::{env, fs}; + +use cfg_aliases::cfg_aliases; +#[cfg(feature = "_rt1xxx")] +use nxp_pac::metadata; +#[allow(unused)] +use proc_macro2::TokenStream; +#[allow(unused)] +use quote::quote; + +#[path = "./build_common.rs"] +mod common; + +fn main() { + let mut cfgs = common::CfgSet::new(); + common::set_target_cfgs(&mut cfgs); + + let chip_name = match env::vars() + .map(|(a, _)| a) + .filter(|x| x.starts_with("CARGO_FEATURE_MIMXRT") || x.starts_with("CARGO_FEATURE_LPC")) + .get_one() + { + Ok(x) => x, + Err(GetOneError::None) => panic!("No mimxrt/lpc Cargo feature enabled"), + Err(GetOneError::Multiple) => panic!("Multiple mimxrt/lpc Cargo features enabled"), + } + .strip_prefix("CARGO_FEATURE_") + .unwrap() + .to_ascii_lowercase(); + + cfg_aliases! { + rt1xxx: { feature = "mimxrt1011" }, + gpio1: { feature = "mimxrt1011" }, + gpio2: { feature = "mimxrt1011" }, + gpio5: { feature = "mimxrt1011" }, + } + + eprintln!("chip: {chip_name}"); + + generate_code(); +} + +#[cfg(feature = "_rt1xxx")] +fn generate_iomuxc() -> TokenStream { + use proc_macro2::{Ident, Span}; + + let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { + let name = Ident::new(®isters.name, Span::call_site()); + let address = registers.pad_ctl; + + quote! { + pub const #name: u32 = #address; + } + }); + + let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { + let name = Ident::new(®isters.name, Span::call_site()); + let address = registers.mux_ctl; + + quote! { + pub const #name: u32 = #address; + } + }); + + quote! { + pub mod iomuxc { + pub mod pads { + #(#pads)* + } + + pub mod muxes { + #(#muxes)* + } + } + } +} + +fn generate_code() { + #[allow(unused)] + use std::fmt::Write; + + let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + #[allow(unused_mut)] + let mut output = String::new(); + + #[cfg(feature = "_rt1xxx")] + writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); + + let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); + fs::write(&out_file, output).unwrap(); + rustfmt(&out_file); +} + +/// rustfmt a given path. +/// Failures are logged to stderr and ignored. +fn rustfmt(path: impl AsRef) { + let path = path.as_ref(); + match Command::new("rustfmt").args([path]).output() { + Err(e) => { + eprintln!("failed to exec rustfmt {:?}: {:?}", path, e); + } + Ok(out) => { + if !out.status.success() { + eprintln!("rustfmt {:?} failed:", path); + eprintln!("=== STDOUT:"); + std::io::stderr().write_all(&out.stdout).unwrap(); + eprintln!("=== STDERR:"); + std::io::stderr().write_all(&out.stderr).unwrap(); + } + } + } +} + +enum GetOneError { + None, + Multiple, +} + +trait IteratorExt: Iterator { + fn get_one(self) -> Result; +} + +impl IteratorExt for T { + fn get_one(mut self) -> Result { + match self.next() { + None => Err(GetOneError::None), + Some(res) => match self.next() { + Some(_) => Err(GetOneError::Multiple), + None => Ok(res), + }, + } + } +} diff --git a/embassy-nxp/build_common.rs b/embassy-nxp/build_common.rs new file mode 100644 index 000000000..4f24e6d37 --- /dev/null +++ b/embassy-nxp/build_common.rs @@ -0,0 +1,94 @@ +// NOTE: this file is copy-pasted between several Embassy crates, because there is no +// straightforward way to share this code: +// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path = +// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate +// reside in the crate's directory, +// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because +// symlinks don't work on Windows. + +use std::collections::HashSet; +use std::env; + +/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring +/// them (`cargo:rust-check-cfg=cfg(X)`). +#[derive(Debug)] +pub struct CfgSet { + enabled: HashSet, + declared: HashSet, +} + +impl CfgSet { + pub fn new() -> Self { + Self { + enabled: HashSet::new(), + declared: HashSet::new(), + } + } + + /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation. + /// + /// All configs that can potentially be enabled should be unconditionally declared using + /// [`Self::declare()`]. + pub fn enable(&mut self, cfg: impl AsRef) { + if self.enabled.insert(cfg.as_ref().to_owned()) { + println!("cargo:rustc-cfg={}", cfg.as_ref()); + } + } + + pub fn enable_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.enable(cfg.as_ref()); + } + } + + /// Declare a valid config for conditional compilation, without enabling it. + /// + /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid. + pub fn declare(&mut self, cfg: impl AsRef) { + if self.declared.insert(cfg.as_ref().to_owned()) { + println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref()); + } + } + + pub fn declare_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.declare(cfg.as_ref()); + } + } + + pub fn set(&mut self, cfg: impl Into, enable: bool) { + let cfg = cfg.into(); + if enable { + self.enable(cfg.clone()); + } + self.declare(cfg); + } +} + +/// Sets configs that describe the target platform. +pub fn set_target_cfgs(cfgs: &mut CfgSet) { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + cfgs.enable_all(&["cortex_m", "armv6m"]); + } else if target.starts_with("thumbv7m-") { + cfgs.enable_all(&["cortex_m", "armv7m"]); + } else if target.starts_with("thumbv7em-") { + cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]); + } else if target.starts_with("thumbv8m.base") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]); + } else if target.starts_with("thumbv8m.main") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]); + } + cfgs.declare_all(&[ + "cortex_m", + "armv6m", + "armv7m", + "armv7em", + "armv8m", + "armv8m_base", + "armv8m_main", + ]); + + cfgs.set("has_fpu", target.ends_with("-eabihf")); +} diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs new file mode 100644 index 000000000..a74d953fc --- /dev/null +++ b/embassy-nxp/src/chips/mimxrt1011.rs @@ -0,0 +1,113 @@ +// This must be imported so that __preinit is defined. +use imxrt_rt as _; +pub use nxp_pac as pac; + +embassy_hal_internal::peripherals! { + // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other + // peripheral types (e.g. I2C). + GPIO_00, + GPIO_01, + GPIO_02, + GPIO_03, + GPIO_04, + GPIO_05, + GPIO_06, + GPIO_07, + GPIO_08, + GPIO_09, + GPIO_10, + GPIO_11, + GPIO_12, + GPIO_13, + GPIO_AD_00, + GPIO_AD_01, + GPIO_AD_02, + GPIO_AD_03, + GPIO_AD_04, + GPIO_AD_05, + GPIO_AD_06, + GPIO_AD_07, + GPIO_AD_08, + GPIO_AD_09, + GPIO_AD_10, + GPIO_AD_11, + GPIO_AD_12, + GPIO_AD_13, + GPIO_AD_14, + GPIO_SD_00, + GPIO_SD_01, + GPIO_SD_02, + GPIO_SD_03, + GPIO_SD_04, + GPIO_SD_05, + GPIO_SD_06, + GPIO_SD_07, + GPIO_SD_08, + GPIO_SD_09, + GPIO_SD_10, + GPIO_SD_11, + GPIO_SD_12, + GPIO_SD_13, + PMIC_ON_REQ, +} + +impl_gpio! { + // GPIO Bank 1 + GPIO_00(Gpio1, 0); + GPIO_01(Gpio1, 1); + GPIO_02(Gpio1, 2); + GPIO_03(Gpio1, 3); + GPIO_04(Gpio1, 4); + GPIO_05(Gpio1, 5); + GPIO_06(Gpio1, 6); + GPIO_07(Gpio1, 7); + GPIO_08(Gpio1, 8); + GPIO_09(Gpio1, 9); + GPIO_10(Gpio1, 10); + GPIO_11(Gpio1, 11); + GPIO_12(Gpio1, 12); + GPIO_13(Gpio1, 13); + GPIO_AD_00(Gpio1, 14); + GPIO_AD_01(Gpio1, 15); + GPIO_AD_02(Gpio1, 16); + GPIO_AD_03(Gpio1, 17); + GPIO_AD_04(Gpio1, 18); + GPIO_AD_05(Gpio1, 19); + GPIO_AD_06(Gpio1, 20); + GPIO_AD_07(Gpio1, 21); + GPIO_AD_08(Gpio1, 22); + GPIO_AD_09(Gpio1, 23); + GPIO_AD_10(Gpio1, 24); + GPIO_AD_11(Gpio1, 25); + GPIO_AD_12(Gpio1, 26); + GPIO_AD_13(Gpio1, 27); + GPIO_AD_14(Gpio1, 28); + + // GPIO Bank 2 + GPIO_SD_00(Gpio2, 0); + GPIO_SD_01(Gpio2, 1); + GPIO_SD_02(Gpio2, 2); + GPIO_SD_03(Gpio2, 3); + GPIO_SD_04(Gpio2, 4); + GPIO_SD_05(Gpio2, 5); + GPIO_SD_06(Gpio2, 6); + GPIO_SD_07(Gpio2, 7); + GPIO_SD_08(Gpio2, 8); + GPIO_SD_09(Gpio2, 9); + GPIO_SD_10(Gpio2, 10); + GPIO_SD_11(Gpio2, 11); + GPIO_SD_12(Gpio2, 12); + GPIO_SD_13(Gpio2, 13); + + // GPIO Bank 5 + PMIC_ON_REQ(Gpio5, 0); +} + +pub(crate) mod _generated { + #![allow(dead_code)] + #![allow(unused_imports)] + #![allow(non_snake_case)] + #![allow(missing_docs)] + + include!(concat!(env!("OUT_DIR"), "/_generated.rs")); +} diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs index 809903d97..3049cc12d 100644 --- a/embassy-nxp/src/gpio.rs +++ b/embassy-nxp/src/gpio.rs @@ -1,5 +1,7 @@ //! General purpose input/output (GPIO) driver. +#![macro_use] #[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] +#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] mod inner; pub use inner::*; diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs new file mode 100644 index 000000000..9c58e8a7d --- /dev/null +++ b/embassy-nxp/src/gpio/rt1xxx.rs @@ -0,0 +1,895 @@ +#![macro_use] + +use core::future::Future; +use core::ops::Not; +use core::pin::Pin as FuturePin; +use core::task::{Context, Poll}; + +use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +use nxp_pac::gpio::vals::Icr; +use nxp_pac::iomuxc::vals::Pus; + +use crate::chip::{mux_address, pad_address}; +use crate::pac::common::{Reg, RW}; +#[cfg(feature = "rt")] +use crate::pac::interrupt; +use crate::pac::iomuxc::regs::{Ctl, MuxCtl}; +#[cfg(gpio5)] +use crate::pac::{self, gpio::Gpio}; + +/// The GPIO pin level for pins set on "Digital" mode. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Level { + /// Logical low. Corresponds to 0V. + Low, + /// Logical high. Corresponds to VDD. + High, +} + +impl From for Level { + fn from(val: bool) -> Self { + match val { + true => Self::High, + false => Self::Low, + } + } +} + +impl From for bool { + fn from(level: Level) -> bool { + match level { + Level::Low => false, + Level::High => true, + } + } +} + +impl Not for Level { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Level::Low => Level::High, + Level::High => Level::Low, + } + } +} + +/// Pull setting for a GPIO input set on "Digital" mode. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Pull { + /// No pull. + None, + + // TODO: What Does PUE::KEEPER mean here? + + // 22 kOhm pull-up resistor. + Up22K, + + // 47 kOhm pull-up resistor. + Up47K, + + // 100 kOhm pull-up resistor. + Up100K, + + // 100 kOhm pull-down resistor. + Down100K, +} + +/// Drive strength of an output +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Drive { + Disabled, + _150R, + _75R, + _50R, + _37R, + _30R, + _25R, + _20R, +} + +/// Slew rate of an output +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SlewRate { + Slow, + + Fast, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Bank { + /// Bank 1 + #[cfg(gpio1)] + Gpio1, + + /// Bank 2 + #[cfg(gpio2)] + Gpio2, + + /// Bank 5 + #[cfg(gpio5)] + Gpio5, +} + +/// GPIO flexible pin. +/// +/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain +/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output +/// mode. +pub struct Flex<'d> { + pub(crate) pin: Peri<'d, AnyPin>, +} + +impl<'d> Flex<'d> { + /// Wrap the pin in a `Flex`. + /// + /// The pin remains disconnected. The initial output level is unspecified, but can be changed + /// before the pin is put into output mode. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>) -> Self { + Self { pin: pin.into() } + } + + /// Set the pin's pull. + #[inline] + pub fn set_pull(&mut self, pull: Pull) { + let (pke, pue, pus) = match pull { + Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN), + Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP), + Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP), + Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP), + Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN), + }; + + self.pin.pad().modify(|w| { + w.set_pke(pke); + w.set_pue(pue); + w.set_pus(pus); + }); + } + + // Set the pin's slew rate. + #[inline] + pub fn set_slewrate(&mut self, rate: SlewRate) { + self.pin.pad().modify(|w| { + w.set_sre(match rate { + SlewRate::Slow => false, + SlewRate::Fast => true, + }); + }); + } + + /// Set the pin's Schmitt trigger. + #[inline] + pub fn set_schmitt(&mut self, enable: bool) { + self.pin.pad().modify(|w| { + w.set_hys(enable); + }); + } + + /// Put the pin into input mode. + /// + /// The pull setting is left unchanged. + #[inline] + pub fn set_as_input(&mut self) { + self.pin.mux().modify(|w| { + w.set_mux_mode(GPIO_MUX_MODE); + }); + + // Setting direction is RMW + critical_section::with(|_cs| { + self.pin.block().gdir().modify(|w| { + w.set_gdir(self.pin.pin_number() as usize, false); + }); + }) + } + + /// Put the pin into output mode. + /// + /// The pin level will be whatever was set before (or low by default). If you want it to begin + /// at a specific level, call `set_high`/`set_low` on the pin first. + #[inline] + pub fn set_as_output(&mut self) { + self.pin.mux().modify(|w| { + w.set_mux_mode(GPIO_MUX_MODE); + }); + + // Setting direction is RMW + critical_section::with(|_cs| { + self.pin.block().gdir().modify(|w| { + w.set_gdir(self.pin.pin_number() as usize, true); + }); + }) + } + + /// Put the pin into input + open-drain output mode. + /// + /// The hardware will drive the line low if you set it to low, and will leave it floating if you set + /// it to high, in which case you can read the input to figure out whether another device + /// is driving the line low. + /// + /// The pin level will be whatever was set before (or low by default). If you want it to begin + /// at a specific level, call `set_high`/`set_low` on the pin first. + /// + /// The internal weak pull-up and pull-down resistors will be disabled. + #[inline] + pub fn set_as_input_output(&mut self) { + self.pin.pad().modify(|w| { + w.set_ode(true); + }); + } + + /// Set the pin as "disconnected", ie doing nothing and consuming the lowest + /// amount of power possible. + /// + /// This is currently the same as [`Self::set_as_analog()`] but is semantically different + /// really. Drivers should `set_as_disconnected()` pins when dropped. + /// + /// Note that this also disables the pull-up and pull-down resistors. + #[inline] + pub fn set_as_disconnected(&mut self) { + self.pin.pad().modify(|w| { + w.set_ode(false); + w.set_pke(false); + w.set_pue(false); + w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN); + }); + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + self.pin.block().psr().read().psr(self.pin.pin_number() as usize) + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + !self.is_high() + } + + /// Returns current pin level + #[inline] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.block().dr_set().write(|w| { + w.set_dr_set(self.pin.pin_number() as usize, true); + }); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.block().dr_clear().write(|w| { + w.set_dr_clear(self.pin.pin_number() as usize, true); + }); + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.block().dr_toggle().write(|w| { + w.set_dr_toggle(self.pin.pin_number() as usize, true); + }); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + match level { + Level::Low => self.set_low(), + Level::High => self.set_high(), + } + } + + /// Get the current pin output level. + #[inline] + pub fn get_output_level(&self) -> Level { + self.is_set_high().into() + } + + /// Is the output level high? + /// + /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`]. + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.block().dr().read().dr(self.pin.pin_number() as usize) + } + + /// Is the output level low? + /// + /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`]. + #[inline] + pub fn is_set_low(&self) -> bool { + !self.is_set_high() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + pub async fn wait_for_high(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await + } + + /// Wait until the pin is low. If it is already low, return immediately. + #[inline] + pub async fn wait_for_low(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await + } + + /// Wait for the pin to undergo a transition from low to high. + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await + } + + /// Wait for the pin to undergo a transition from high to low. + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await + } + + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await + } +} + +impl<'d> Drop for Flex<'d> { + fn drop(&mut self) { + self.set_as_disconnected(); + } +} + +/// GPIO input driver. +pub struct Input<'d> { + pin: Flex<'d>, +} + +impl<'d> Input<'d> { + /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_input(); + pin.set_pull(pull); + Self { pin } + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + self.pin.is_high() + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } + + /// Get the current pin input level. + #[inline] + pub fn get_level(&self) -> Level { + self.pin.get_level() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + 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. + #[inline] + 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. + #[inline] + 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. + #[inline] + 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. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await + } +} + +/// GPIO output driver. +/// +/// Note that pins will **return to their floating state** when `Output` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `Output`, or pass it to [`core::mem::forget`]. +pub struct Output<'d> { + pin: Flex<'d>, +} + +impl<'d> Output<'d> { + /// Create GPIO output driver for a [Pin] with the provided [Level] configuration. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_output(); + pin.set_level(initial_output); + Self { pin } + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_high(); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_low(); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + self.pin.set_level(level) + } + + /// Is the output pin set as high? + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Is the output pin set as low? + #[inline] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() + } + + /// What level output is set to + #[inline] + pub fn get_output_level(&self) -> Level { + self.pin.get_output_level() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle(); + } +} + +/// GPIO output open-drain driver. +/// +/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `OutputOpenDrain`, or pass it to [`core::mem::forget`]. +pub struct OutputOpenDrain<'d> { + pin: Flex<'d>, +} + +impl<'d> OutputOpenDrain<'d> { + /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level]. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_level(initial_output); + pin.set_as_input_output(); + Self { pin } + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + !self.pin.is_low() + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } + + /// Get the current pin input level. + #[inline] + pub fn get_level(&self) -> Level { + self.pin.get_level() + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_high(); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_low(); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + self.pin.set_level(level); + } + + /// Get whether the output level is set to high. + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Get whether the output level is set to low. + #[inline] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() + } + + /// Get the current output level. + #[inline] + pub fn get_output_level(&self) -> Level { + self.pin.get_output_level() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + 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. + #[inline] + 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. + #[inline] + 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. + #[inline] + 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. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await + } +} + +#[allow(private_bounds)] +pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { + /// Returns the pin number within a bank + #[inline] + fn pin(&self) -> u8 { + self.pin_number() + } + + #[inline] + fn bank(&self) -> Bank { + self._bank() + } +} + +/// Type-erased GPIO pin. +pub struct AnyPin { + pub(crate) pin_number: u8, + pub(crate) bank: Bank, +} + +impl AnyPin { + /// Unsafely create a new type-erased pin. + /// + /// # Safety + /// + /// You must ensure that you’re only using one instance of this type at a time. + pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> { + Peri::new_unchecked(Self { pin_number, bank }) + } +} + +impl_peripheral!(AnyPin); + +impl Pin for AnyPin {} +impl SealedPin for AnyPin { + #[inline] + fn pin_number(&self) -> u8 { + self.pin_number + } + + #[inline] + fn _bank(&self) -> Bank { + self.bank + } +} + +// Impl details + +/// Mux mode for GPIO pins. This is constant across all RT1xxx parts. +const GPIO_MUX_MODE: u8 = 0b101; + +// FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14. +#[cfg(gpio1)] +static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio2)] +static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio5)] +static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; + +/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. +pub(crate) trait SealedPin: Sized { + fn pin_number(&self) -> u8; + + fn _bank(&self) -> Bank; + + #[inline] + fn block(&self) -> Gpio { + match self._bank() { + #[cfg(gpio1)] + Bank::Gpio1 => pac::GPIO1, + #[cfg(gpio2)] + Bank::Gpio2 => pac::GPIO2, + #[cfg(gpio5)] + Bank::Gpio5 => pac::GPIO5, + } + } + + #[inline] + fn mux(&self) -> Reg { + // SAFETY: The generated mux address table is valid since it is generated from the SVD files. + let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() }; + + // SAFETY: The register at the address is an instance of MuxCtl. + unsafe { Reg::from_ptr(address as *mut _) } + } + + #[inline] + fn pad(&self) -> Reg { + // SAFETY: The generated pad address table is valid since it is generated from the SVD files. + let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() }; + + // SAFETY: The register at the address is an instance of Ctl. + unsafe { Reg::from_ptr(address as *mut _) } + } + + fn waker(&self) -> &AtomicWaker { + match self._bank() { + #[cfg(gpio1)] + Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize], + #[cfg(gpio2)] + Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize], + #[cfg(gpio5)] + Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], + } + } +} + +/// This enum matches the layout of Icr. +enum InterruptConfiguration { + Low, + High, + RisingEdge, + FallingEdge, + AnyEdge, +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct InputFuture<'d> { + pin: Peri<'d, AnyPin>, +} + +impl<'d> InputFuture<'d> { + fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self { + let block = pin.block(); + + let (icr, edge_sel) = match config { + InterruptConfiguration::Low => (Icr::LOW_LEVEL, false), + InterruptConfiguration::High => (Icr::HIGH_LEVEL, false), + InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false), + InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false), + InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true), + }; + + let index = if pin.pin_number() > 15 { 1 } else { 0 }; + + // Interrupt configuration performs RMW + critical_section::with(|_cs| { + // Disable interrupt so a level/edge detection change does not cause ISR to be set. + block.imr().modify(|w| { + w.set_imr(pin.pin_number() as usize, false); + }); + + block.icr(index).modify(|w| { + w.set_pin(pin.pin_number() as usize, icr); + }); + + block.edge_sel().modify(|w| { + w.set_edge_sel(pin.pin_number() as usize, edge_sel); + }); + + // Clear the previous interrupt. + block.isr().modify(|w| { + // "Status flags are cleared by writing a 1 to the corresponding bit position." + w.set_isr(pin.pin_number() as usize, true); + }); + }); + + Self { pin } + } +} + +impl<'d> Future for InputFuture<'d> { + type Output = (); + + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // We need to register/re-register the waker for each poll because any + // calls to wake will deregister the waker. + let waker = self.pin.waker(); + waker.register(cx.waker()); + + // Enabling interrupt is RMW + critical_section::with(|_cs| { + self.pin.block().imr().modify(|w| { + w.set_imr(self.pin.pin_number() as usize, true); + }); + }); + + let isr = self.pin.block().isr().read(); + + if isr.isr(self.pin.pin_number() as usize) { + return Poll::Ready(()); + } + + Poll::Pending + } +} + +/// A macro to generate all GPIO pins. +/// +/// This generates a lookup table for IOMUX register addresses. +macro_rules! impl_gpio { + ( + $($name: ident($bank: ident, $pin_number: expr);)* + ) => { + #[inline] + pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option { + match (bank, pin) { + $( + (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name), + )* + _ => None + } + } + + #[inline] + pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option { + match (bank, pin) { + $( + (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name), + )* + _ => None + } + } + + $( + impl_pin!($name, $bank, $pin_number); + )* + }; +} + +macro_rules! impl_pin { + ($name: ident, $bank: ident, $pin_num: expr) => { + impl crate::gpio::Pin for crate::peripherals::$name {} + impl crate::gpio::SealedPin for crate::peripherals::$name { + #[inline] + fn pin_number(&self) -> u8 { + $pin_num + } + + #[inline] + fn _bank(&self) -> crate::gpio::Bank { + crate::gpio::Bank::$bank + } + } + + impl From for crate::gpio::AnyPin { + fn from(val: peripherals::$name) -> Self { + use crate::gpio::SealedPin; + + Self { + pin_number: val.pin_number(), + bank: val._bank(), + } + } + } + }; +} + +pub(crate) fn init() { + #[cfg(feature = "rt")] + unsafe { + use embassy_hal_internal::interrupt::InterruptExt; + + pac::Interrupt::GPIO1_COMBINED_0_15.enable(); + pac::Interrupt::GPIO1_COMBINED_16_31.enable(); + pac::Interrupt::GPIO2_COMBINED_0_15.enable(); + pac::Interrupt::GPIO5_COMBINED_0_15.enable(); + } +} + +/// IRQ handler for GPIO pins. +/// +/// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt +/// is for pins 16 through 31 +#[cfg(feature = "rt")] +fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) { + use crate::BitIter; + + let isr = block.isr().read().0; + let imr = block.imr().read().0; + let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF }; + let bits = isr & imr & mask; + + for bit in BitIter(bits) { + wakers[bit as usize].wake(); + + // Disable further interrupts for this pin. The input future will check ISR (which is kept + // until reset). + block.imr().modify(|w| { + w.set_imr(bit as usize, false); + }); + } +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO1_COMBINED_0_15() { + irq_handler(pac::GPIO1, &GPIO1_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO1_COMBINED_16_31() { + irq_handler(pac::GPIO1, &GPIO1_WAKERS, true); +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO2_COMBINED_0_15() { + irq_handler(pac::GPIO2, &GPIO2_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO5_COMBINED_0_15() { + irq_handler(pac::GPIO5, &GPIO5_WAKERS, false); +} diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 1abaca708..a715770c4 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -1,12 +1,19 @@ #![no_std] -pub mod fmt; +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; +#[cfg(feature = "_time_driver")] +#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] +mod time_driver; + // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] +#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] mod chip; #[cfg(feature = "unstable-pac")] @@ -22,13 +29,66 @@ pub use embassy_hal_internal::{Peri, PeripheralType}; /// /// This should only be called once and at startup, otherwise it panics. pub fn init(_config: config::Config) -> Peripherals { - #[cfg(feature = "lpc55")] + // Do this first, so that it panics if user is calling `init` a second time + // before doing anything important. + let peripherals = Peripherals::take(); + + #[cfg(feature = "mimxrt1011")] { - gpio::init(); - pint::init(); + // The RT1010 Reference manual states that core clock root must be switched before + // reprogramming PLL2. + pac::CCM.cbcdr().modify(|w| { + w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1); + }); + + while matches!( + pac::CCM.cdhipr().read().periph_clk_sel_busy(), + pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1 + ) {} + + info!("Core clock root switched"); + + // 480 * 18 / 24 = 360 + pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12)); + + //480*18/24(pfd0)/4 + pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24)); + pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into())); + + // CPU Core + pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18)); + cortex_m::asm::delay(500_000); + + // Clock core clock with PLL 2. + pac::CCM + .cbcdr() + .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false + + while matches!( + pac::CCM.cdhipr().read().periph_clk_sel_busy(), + pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1 + ) {} + + pac::CCM + .cbcmr() + .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0)); + + // TODO: Some for USB PLLs + + // DCDC clock? + pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); } - crate::Peripherals::take() + #[cfg(any(feature = "lpc55", rt1xxx))] + gpio::init(); + + #[cfg(feature = "lpc55")] + pint::init(); + + #[cfg(feature = "_time_driver")] + time_driver::init(); + + peripherals } /// HAL configuration for the NXP board. @@ -36,3 +96,20 @@ pub mod config { #[derive(Default)] pub struct Config {} } + +#[allow(unused)] +struct BitIter(u32); + +impl Iterator for BitIter { + type Item = u32; + + fn next(&mut self) -> Option { + match self.0.trailing_zeros() { + 32 => None, + b => { + self.0 &= !(1 << b); + Some(b) + } + } + } +} diff --git a/embassy-nxp/src/time_driver/pit.rs b/embassy-nxp/src/time_driver/pit.rs new file mode 100644 index 000000000..985e5e815 --- /dev/null +++ b/embassy-nxp/src/time_driver/pit.rs @@ -0,0 +1,187 @@ +//! Time driver using Periodic Interrupt Timer (PIT) +//! +//! This driver is used with the iMXRT1xxx parts. +//! +//! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer. +//! The 64-bit timer is used to track how many ticks since boot. +//! +//! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when +//! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to +//! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower. +//! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few +//! microseconds late. +//! +//! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift. + +use core::cell::{Cell, RefCell}; +use core::task::Waker; + +use critical_section::{CriticalSection, Mutex}; +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_time_driver::Driver as _; +use embassy_time_queue_utils::Queue; + +use crate::pac::{self, interrupt}; + +struct Driver { + alarm: Mutex>, + queue: Mutex>, +} + +impl embassy_time_driver::Driver for Driver { + fn now(&self) -> u64 { + loop { + // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the + // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to + // thread which was preempted. + let hi = pac::PIT.ltmr64h().read().lth(); + let lo = pac::PIT.ltmr64l().read().ltl(); + let hi2 = pac::PIT.ltmr64h().read().lth(); + + if hi == hi2 { + // PIT timers always count down. + return u64::MAX - ((hi as u64) << 32 | (lo as u64)); + } + } + } + + fn schedule_wake(&self, at: u64, waker: &Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } +} + +impl Driver { + fn init(&'static self) { + // Disable PIT clock during mux configuration. + pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00)); + + // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you + // could divide the clock root by up to 64. + pac::CCM.cscmr1().modify(|r| { + // 1 MHz + r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24); + r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1); + }); + + pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11)); + + // Disable clock during init. + // + // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else + // you will need to recover the device with boot mode switches when using any PIT registers. + pac::PIT.mcr().modify(|w| { + w.set_mdis(true); + }); + + pac::PIT.timer(0).ldval().write_value(u32::MAX); + pac::PIT.timer(1).ldval().write_value(u32::MAX); + pac::PIT.timer(2).ldval().write_value(0); + pac::PIT.timer(3).ldval().write_value(0); + + pac::PIT.timer(1).tctrl().write(|w| { + // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer. + w.set_chn(true); + w.set_ten(true); + w.set_tie(false); + }); + + pac::PIT.timer(0).tctrl().write(|w| { + w.set_chn(false); + w.set_ten(true); + w.set_tie(false); + }); + + pac::PIT.timer(2).tctrl().write(|w| { + w.set_tie(true); + }); + + unsafe { interrupt::PIT.enable() }; + + pac::PIT.mcr().write(|w| { + w.set_mdis(false); + }); + } + + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + let alarm = self.alarm.borrow(cs); + alarm.set(timestamp); + + let timer = pac::PIT.timer(2); + let now = self.now(); + + if timestamp <= now { + alarm.set(u64::MAX); + + return false; + } + + timer.tctrl().modify(|x| x.set_ten(false)); + timer.tflg().modify(|x| x.set_tif(true)); + + // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later. + timer.ldval().write_value((timestamp - now) as u32); + timer.tctrl().modify(|x| x.set_ten(true)); + + true + } + + fn trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); + + while !self.set_alarm(cs, next) { + next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); + } + } + + fn on_interrupt(&self) { + critical_section::with(|cs| { + let timer = pac::PIT.timer(2); + let alarm = self.alarm.borrow(cs); + let interrupted = timer.tflg().read().tif(); + timer.tflg().write(|r| r.set_tif(true)); + + if interrupted { + // A new load value will not apply until the next timer expiration. + // + // The expiry may be up to u32::MAX cycles away, so the timer must be restarted. + timer.tctrl().modify(|r| r.set_ten(false)); + + let now = self.now(); + let timestamp = alarm.get(); + + if timestamp <= now { + self.trigger_alarm(cs); + } else { + // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm. + timer.ldval().write_value((timestamp - now) as u32); + timer.tctrl().modify(|r| r.set_ten(true)); + } + } + }); + } +} + +embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver { + alarm: Mutex::new(Cell::new(0)), + queue: Mutex::new(RefCell::new(Queue::new())) +}); + +pub(crate) fn init() { + DRIVER.init(); +} + +#[cfg(feature = "rt")] +#[interrupt] +fn PIT() { + DRIVER.on_interrupt(); +} diff --git a/examples/mimxrt1011/.cargo/config.toml b/examples/mimxrt1011/.cargo/config.toml new file mode 100644 index 000000000..12f4b27b2 --- /dev/null +++ b/examples/mimxrt1011/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip MIMXRT1010' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M7 + +[env] +DEFMT_LOG = "trace" diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml new file mode 100644 index 000000000..cf4e4c163 --- /dev/null +++ b/examples/mimxrt1011/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "embassy-imxrt1011-examples" +version = "0.1.0" +edition = "2021" +license = "MIT or Apache-2.0" + +[dependencies] +cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.3" +defmt = "1.0.1" +defmt-rtt = "1.0.0" + +embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } +embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1011", "unstable-pac", "time-driver-pit"] } +embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime" # RT1011 hard faults currently with this enabled. +embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = "1.0.0" + +imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1010"] } +panic-probe = { version = "1.0.0", features = ["print-defmt"] } +panic-semihosting = "0.6.0" + +[build-dependencies] +imxrt-rt = { version = "0.1.7", features = ["device"] } + +[profile.release] +debug = 2 diff --git a/examples/mimxrt1011/build.rs b/examples/mimxrt1011/build.rs new file mode 100644 index 000000000..99e172aba --- /dev/null +++ b/examples/mimxrt1011/build.rs @@ -0,0 +1,14 @@ +use imxrt_rt::{Family, RuntimeBuilder}; + +fn main() { + // The IMXRT1010-EVK technically has 128M of flash, but we only ever use 8MB so that the examples + // will build fine on the Adafruit Metro M7 boards. + RuntimeBuilder::from_flexspi(Family::Imxrt1010, 8 * 1024 * 1024) + .build() + .unwrap(); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + // Not link.x, as imxrt-rt needs to do some special things + println!("cargo:rustc-link-arg-bins=-Timxrt-link.x"); +} diff --git a/examples/mimxrt1011/src/bin/blinky.rs b/examples/mimxrt1011/src/bin/blinky.rs new file mode 100644 index 000000000..a5d5de6b3 --- /dev/null +++ b/examples/mimxrt1011/src/bin/blinky.rs @@ -0,0 +1,48 @@ +//! This example works on the following boards: +//! - IMXRT1010-EVK +//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button +//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this) +//! +//! Although beware you will need to change the GPIO pins being used (scroll down). + +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nxp::gpio::{Level, Output}; +use embassy_time::Timer; +// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked. +use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_nxp::init(Default::default()); + info!("Hello world!"); + + /* Pick the pins to use depending on your board. */ + + // IMXRT1010-EVK + // + // LED (D25) + let led = p.GPIO_11; + + // Adafruit Metro M7 (both microSD and AirLift variants) + // + // The LED is connected to D13 on the board. + // let led = p.GPIO_03; + + // Makerdiary iMX RT1011 Nano Kit + // + // LED0 + // let led = p.GPIO_SD_04; + + let mut led = Output::new(led, Level::Low); + + loop { + Timer::after_millis(500).await; + + info!("Toggle"); + led.toggle(); + } +} diff --git a/examples/mimxrt1011/src/bin/button.rs b/examples/mimxrt1011/src/bin/button.rs new file mode 100644 index 000000000..e63d7171d --- /dev/null +++ b/examples/mimxrt1011/src/bin/button.rs @@ -0,0 +1,62 @@ +//! This example works on the following boards: +//! - IMXRT1010-EVK +//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button +//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this) +//! +//! Although beware you will need to change the GPIO pins being used (scroll down). + +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nxp::gpio::{Input, Level, Output, Pull}; +// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked. +use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_nxp::init(Default::default()); + info!("Hello world!"); + + /* Pick the pins to use depending on your board. */ + + // IMXRT1010-EVK + // + // LED (D25) and user button (SW4) + let (led, button) = (p.GPIO_11, p.GPIO_SD_05); + + // Adafruit Metro M7 (both microSD and AirLift variants) + // + // The LED is connected to D13 on the board. + // + // In particular the Metro M7 has no board user buttons, so you will need to connect a button. + // Any other GPIO pin can be used. GPIO_04 is used for example since it is on pin D12. + // let (led, button) = (p.GPIO_03, p.GPIO_04); + + // Makerdiary iMX RT1011 Nano Kit + // + // LED0 and user button. + // let (led, button) = (p.GPIO_SD_04, p.GPIO_SD_03); + + let mut button = Input::new(button, Pull::Up100K); + let mut led = Output::new(led, Level::Low); + led.set_high(); + + loop { + button.wait_for_falling_edge().await; + + info!("Toggled"); + led.toggle(); + + // The RT1010EVK has a 100 nF debouncing capacitor which results in false positive events + // when listening for a falling edge in a loop, wait for the rising edge and then wait for + // stabilization. + button.wait_for_rising_edge().await; + + // Stabilization. + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + } +} diff --git a/examples/mimxrt1011/src/lib.rs b/examples/mimxrt1011/src/lib.rs new file mode 100644 index 000000000..f0391ef57 --- /dev/null +++ b/examples/mimxrt1011/src/lib.rs @@ -0,0 +1,75 @@ +//! FlexSPI configuration block (FCB) for iMXRT1011 boards. +//! +//! This is a generic FCB that should work with most QSPI flash. + +#![no_std] + +use imxrt_boot_gen::flexspi; +use imxrt_boot_gen::flexspi::opcodes::sdr::*; +use imxrt_boot_gen::flexspi::{ + ColumnAddressWidth, Command, DeviceModeConfiguration, FlashPadType, Instr, LookupTable, Pads, + ReadSampleClockSource, Sequence, SequenceBuilder, SerialClockFrequency, SerialFlashRegion, + WaitTimeConfigurationCommands, +}; +use imxrt_boot_gen::serial_flash::nor; + +/// While the IMXRT1010-EVK and Makerdiary iMX RT1011 Nano Kit have 128MBit of flash we limit to 64Mbit +/// to allow the Metro M7 boards to use the same FCB configuration. +const DENSITY_BITS: u32 = 64 * 1024 * 1024; +const DENSITY_BYTES: u32 = DENSITY_BITS / 8; + +const SEQ_READ: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0xEB)) + .instr(Instr::new(RADDR, Pads::Four, 0x18)) + .instr(Instr::new(DUMMY, Pads::Four, 0x06)) + .instr(Instr::new(READ, Pads::Four, 0x04)) + .build(); + +const SEQ_READ_STATUS: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0x05)) + .instr(Instr::new(READ, Pads::One, 0x01)) + .build(); + +const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build(); + +const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0x20)) + .instr(Instr::new(RADDR, Pads::One, 0x18)) + .build(); + +const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0x02)) + .instr(Instr::new(RADDR, Pads::One, 0x18)) + .instr(Instr::new(WRITE, Pads::One, 0x04)) + .build(); + +const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build(); + +const LUT: LookupTable = LookupTable::new() + .command(Command::Read, SEQ_READ) + .command(Command::ReadStatus, SEQ_READ_STATUS) + .command(Command::WriteEnable, SEQ_WRITE_ENABLE) + .command(Command::EraseSector, SEQ_ERASE_SECTOR) + .command(Command::PageProgram, SEQ_PAGE_PROGRAM) + .command(Command::ChipErase, SEQ_CHIP_ERASE); + +const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT) + .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad) + .cs_hold_time(0x03) + .cs_setup_time(0x03) + .column_address_width(ColumnAddressWidth::OtherDevices) + .device_mode_configuration(DeviceModeConfiguration::Disabled) + .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable()) + .flash_size(SerialFlashRegion::A1, DENSITY_BYTES) + .serial_clk_freq(SerialClockFrequency::MHz120) + .serial_flash_pad_type(FlashPadType::Quad); + +pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = + nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK) + .page_size(256) + .sector_size(4096) + .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30); + +#[unsafe(no_mangle)] +#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")] +pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK; -- cgit From 03b86d75b6861ed8151f4f48682e9b4f5b159232 Mon Sep 17 00:00:00 2001 From: dimi Date: Tue, 22 Jul 2025 18:16:11 +0200 Subject: derive Copy, Clone for adc config enums --- embassy-stm32/src/adc/adc4.rs | 4 +++- embassy-stm32/src/adc/c0.rs | 3 ++- embassy-stm32/src/adc/f3_v1_1.rs | 2 ++ embassy-stm32/src/adc/v4.rs | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 98483489f..31cbdc0d7 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -76,12 +76,14 @@ impl SealedAdcChannel for Vcore { } } +#[derive(Copy, Clone)] pub enum DacChannel { OUT1, OUT2, } /// Number of samples used for averaging. +#[derive(Copy, Clone)] pub enum Averaging { Disabled, Samples2, @@ -187,7 +189,7 @@ pub struct Adc4<'d, T: Instance> { adc: crate::Peri<'d, T>, } -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub enum Adc4Error { InvalidSequence, DMAError, diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 936ad7413..f5870801e 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -48,7 +48,7 @@ impl SealedAdcChannel for Temperature { } } -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub enum Prescaler { NotDivided, DividedBy2, @@ -138,6 +138,7 @@ impl<'a> defmt::Format for Prescaler { /// Number of samples used for averaging. /// TODO: Implement hardware averaging setting. #[allow(unused)] +#[derive(Copy, Clone)] pub enum Averaging { Disabled, Samples2, diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 944e971bb..84613078c 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -17,6 +17,7 @@ pub const VDDA_CALIB_MV: u32 = 3300; pub const ADC_MAX: u32 = (1 << 12) - 1; pub const VREF_INT: u32 = 1230; +#[derive(Copy, Clone)] pub enum AdcPowerMode { AlwaysOn, DelayOff, @@ -24,6 +25,7 @@ pub enum AdcPowerMode { DelayIdleOff, } +#[derive(Copy, Clone)] pub enum Prescaler { Div1, Div2, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 39e0d51b9..b0871019a 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -142,6 +142,7 @@ impl Prescaler { } /// Number of samples used for averaging. +#[derive(Copy, Clone)] pub enum Averaging { Disabled, Samples2, -- cgit From 1d46f55bddf402c33143959e1ad26af59bb15855 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Mon, 21 Jul 2025 20:29:34 -0500 Subject: nxp: Add mimxrt1062 support The examples in this case are designed for the IMXRT1060-EVK. The same chip is used in the Teensy 4.0/1, but that will probably get its own set of examples due to some differences such as the FCB. --- .vscode/settings.json | 1 + ci.sh | 4 +- embassy-nxp/Cargo.toml | 5 +- embassy-nxp/build.rs | 10 +- embassy-nxp/src/chips/mimxrt1062.rs | 282 +++++++++++++++++++++++++++++ embassy-nxp/src/gpio/rt1xxx.rs | 62 ++++++- embassy-nxp/src/lib.rs | 1 + examples/mimxrt1062-evk/.cargo/config.toml | 8 + examples/mimxrt1062-evk/Cargo.toml | 29 +++ examples/mimxrt1062-evk/build.rs | 12 ++ examples/mimxrt1062-evk/src/bin/blinky.rs | 25 +++ examples/mimxrt1062-evk/src/bin/button.rs | 36 ++++ examples/mimxrt1062-evk/src/lib.rs | 60 ++++++ 13 files changed, 522 insertions(+), 13 deletions(-) create mode 100644 embassy-nxp/src/chips/mimxrt1062.rs create mode 100644 examples/mimxrt1062-evk/.cargo/config.toml create mode 100644 examples/mimxrt1062-evk/Cargo.toml create mode 100644 examples/mimxrt1062-evk/build.rs create mode 100644 examples/mimxrt1062-evk/src/bin/blinky.rs create mode 100644 examples/mimxrt1062-evk/src/bin/button.rs create mode 100644 examples/mimxrt1062-evk/src/lib.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 070e8fbd3..6edd9312a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,6 +37,7 @@ // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/mimxrt1011/Cargo.toml", + // "examples/mimxrt1062-evk/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32c0/Cargo.toml", diff --git a/ci.sh b/ci.sh index e225bc7c9..1a9a1d209 100755 --- a/ci.sh +++ b/ci.sh @@ -181,7 +181,8 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ - --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,defmt,time-driver-pit \ + --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,rt,defmt,time-driver-pit \ + --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1062,rt,defmt,time-driver-pit \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ @@ -266,6 +267,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \ + --- build --release --manifest-path examples/mimxrt1062-evk/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1062-evk \ --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 625906183..293791d34 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -17,13 +17,13 @@ embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-util ## Chip dependencies lpc55-pac = { version = "0.5.0", optional = true } -nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "1e010dbe75ab0e14dd908e4646391403414c8a8e" } +nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" } imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } [build-dependencies] cfg_aliases = "0.2.1" -nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "1e010dbe75ab0e14dd908e4646391403414c8a8e", features = ["metadata"], optional = true } +nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e", features = ["metadata"], optional = true } proc-macro2 = "1.0.95" quote = "1.0.15" @@ -58,3 +58,4 @@ _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] #! ### Chip selection features lpc55 = ["dep:lpc55-pac"] mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] +mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs index 6c10d0e69..f3c062c87 100644 --- a/embassy-nxp/build.rs +++ b/embassy-nxp/build.rs @@ -32,10 +32,12 @@ fn main() { .to_ascii_lowercase(); cfg_aliases! { - rt1xxx: { feature = "mimxrt1011" }, - gpio1: { feature = "mimxrt1011" }, - gpio2: { feature = "mimxrt1011" }, - gpio5: { feature = "mimxrt1011" }, + rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, + gpio1: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, + gpio2: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, + gpio3: { feature = "mimxrt1062" }, + gpio4: { feature = "mimxrt1062" }, + gpio5: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, } eprintln!("chip: {chip_name}"); diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs new file mode 100644 index 000000000..ef153bd66 --- /dev/null +++ b/embassy-nxp/src/chips/mimxrt1062.rs @@ -0,0 +1,282 @@ +// This must be imported so that __preinit is defined. +use imxrt_rt as _; +pub use nxp_pac as pac; + +embassy_hal_internal::peripherals! { + // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other + // peripheral types (e.g. I2C). + GPIO_AD_B0_00, + GPIO_AD_B0_01, + GPIO_AD_B0_02, + GPIO_AD_B0_03, + GPIO_AD_B0_04, + GPIO_AD_B0_05, + GPIO_AD_B0_06, + GPIO_AD_B0_07, + GPIO_AD_B0_08, + GPIO_AD_B0_09, + GPIO_AD_B0_10, + GPIO_AD_B0_11, + GPIO_AD_B0_12, + GPIO_AD_B0_13, + GPIO_AD_B0_14, + GPIO_AD_B0_15, + GPIO_AD_B1_00, + GPIO_AD_B1_01, + GPIO_AD_B1_02, + GPIO_AD_B1_03, + GPIO_AD_B1_04, + GPIO_AD_B1_05, + GPIO_AD_B1_06, + GPIO_AD_B1_07, + GPIO_AD_B1_08, + GPIO_AD_B1_09, + GPIO_AD_B1_10, + GPIO_AD_B1_11, + GPIO_AD_B1_12, + GPIO_AD_B1_13, + GPIO_AD_B1_14, + GPIO_AD_B1_15, + GPIO_B0_00, + GPIO_B0_01, + GPIO_B0_02, + GPIO_B0_03, + GPIO_B0_04, + GPIO_B0_05, + GPIO_B0_06, + GPIO_B0_07, + GPIO_B0_08, + GPIO_B0_09, + GPIO_B0_10, + GPIO_B0_11, + GPIO_B0_12, + GPIO_B0_13, + GPIO_B0_14, + GPIO_B0_15, + GPIO_B1_00, + GPIO_B1_01, + GPIO_B1_02, + GPIO_B1_03, + GPIO_B1_04, + GPIO_B1_05, + GPIO_B1_06, + GPIO_B1_07, + GPIO_B1_08, + GPIO_B1_09, + GPIO_B1_10, + GPIO_B1_11, + GPIO_B1_12, + GPIO_B1_13, + GPIO_B1_14, + GPIO_B1_15, + GPIO_EMC_00, + GPIO_EMC_01, + GPIO_EMC_02, + GPIO_EMC_03, + GPIO_EMC_04, + GPIO_EMC_05, + GPIO_EMC_06, + GPIO_EMC_07, + GPIO_EMC_08, + GPIO_EMC_09, + GPIO_EMC_10, + GPIO_EMC_11, + GPIO_EMC_12, + GPIO_EMC_13, + GPIO_EMC_14, + GPIO_EMC_15, + GPIO_EMC_16, + GPIO_EMC_17, + GPIO_EMC_18, + GPIO_EMC_19, + GPIO_EMC_20, + GPIO_EMC_21, + GPIO_EMC_22, + GPIO_EMC_23, + GPIO_EMC_24, + GPIO_EMC_25, + GPIO_EMC_26, + GPIO_EMC_27, + GPIO_EMC_28, + GPIO_EMC_29, + GPIO_EMC_30, + GPIO_EMC_31, + GPIO_EMC_32, + GPIO_EMC_33, + GPIO_EMC_34, + GPIO_EMC_35, + GPIO_EMC_36, + GPIO_EMC_37, + GPIO_EMC_38, + GPIO_EMC_39, + GPIO_EMC_40, + GPIO_EMC_41, + GPIO_SD_B0_00, + GPIO_SD_B0_01, + GPIO_SD_B0_02, + GPIO_SD_B0_03, + GPIO_SD_B0_04, + GPIO_SD_B0_05, + GPIO_SD_B1_00, + GPIO_SD_B1_01, + GPIO_SD_B1_02, + GPIO_SD_B1_03, + GPIO_SD_B1_04, + GPIO_SD_B1_05, + GPIO_SD_B1_06, + GPIO_SD_B1_07, + GPIO_SD_B1_08, + GPIO_SD_B1_09, + GPIO_SD_B1_10, + GPIO_SD_B1_11, + WAKEUP, + PMIC_ON_REQ, + PMIC_STBY_REQ, +} + +impl_gpio! { + // GPIO Bank 1 + GPIO_AD_B0_00(Gpio1, 0); + GPIO_AD_B0_01(Gpio1, 1); + GPIO_AD_B0_02(Gpio1, 2); + GPIO_AD_B0_03(Gpio1, 3); + GPIO_AD_B0_04(Gpio1, 4); + GPIO_AD_B0_05(Gpio1, 5); + GPIO_AD_B0_06(Gpio1, 6); + GPIO_AD_B0_07(Gpio1, 7); + GPIO_AD_B0_08(Gpio1, 8); + GPIO_AD_B0_09(Gpio1, 9); + GPIO_AD_B0_10(Gpio1, 10); + GPIO_AD_B0_11(Gpio1, 11); + GPIO_AD_B0_12(Gpio1, 12); + GPIO_AD_B0_13(Gpio1, 13); + GPIO_AD_B0_14(Gpio1, 14); + GPIO_AD_B0_15(Gpio1, 15); + GPIO_AD_B1_00(Gpio1, 16); + GPIO_AD_B1_01(Gpio1, 17); + GPIO_AD_B1_02(Gpio1, 18); + GPIO_AD_B1_03(Gpio1, 19); + GPIO_AD_B1_04(Gpio1, 20); + GPIO_AD_B1_05(Gpio1, 21); + GPIO_AD_B1_06(Gpio1, 22); + GPIO_AD_B1_07(Gpio1, 23); + GPIO_AD_B1_08(Gpio1, 24); + GPIO_AD_B1_09(Gpio1, 25); + GPIO_AD_B1_10(Gpio1, 26); + GPIO_AD_B1_11(Gpio1, 27); + GPIO_AD_B1_12(Gpio1, 28); + GPIO_AD_B1_13(Gpio1, 29); + GPIO_AD_B1_14(Gpio1, 30); + GPIO_AD_B1_15(Gpio1, 31); + + // GPIO Bank 2 + GPIO_B0_00(Gpio2, 0); + GPIO_B0_01(Gpio2, 1); + GPIO_B0_02(Gpio2, 2); + GPIO_B0_03(Gpio2, 3); + GPIO_B0_04(Gpio2, 4); + GPIO_B0_05(Gpio2, 5); + GPIO_B0_06(Gpio2, 6); + GPIO_B0_07(Gpio2, 7); + GPIO_B0_08(Gpio2, 8); + GPIO_B0_09(Gpio2, 9); + GPIO_B0_10(Gpio2, 10); + GPIO_B0_11(Gpio2, 11); + GPIO_B0_12(Gpio2, 12); + GPIO_B0_13(Gpio2, 13); + GPIO_B0_14(Gpio2, 14); + GPIO_B0_15(Gpio2, 15); + GPIO_B1_00(Gpio2, 16); + GPIO_B1_01(Gpio2, 17); + GPIO_B1_02(Gpio2, 18); + GPIO_B1_03(Gpio2, 19); + GPIO_B1_04(Gpio2, 20); + GPIO_B1_05(Gpio2, 21); + GPIO_B1_06(Gpio2, 22); + GPIO_B1_07(Gpio2, 23); + GPIO_B1_08(Gpio2, 24); + GPIO_B1_09(Gpio2, 25); + GPIO_B1_10(Gpio2, 26); + GPIO_B1_11(Gpio2, 27); + GPIO_B1_12(Gpio2, 28); + GPIO_B1_13(Gpio2, 29); + GPIO_B1_14(Gpio2, 30); + GPIO_B1_15(Gpio2, 31); + + // GPIO Bank 4 (EMC is 4, then 3) + GPIO_EMC_00(Gpio4, 0); + GPIO_EMC_01(Gpio4, 1); + GPIO_EMC_02(Gpio4, 2); + GPIO_EMC_03(Gpio4, 3); + GPIO_EMC_04(Gpio4, 4); + GPIO_EMC_05(Gpio4, 5); + GPIO_EMC_06(Gpio4, 6); + GPIO_EMC_07(Gpio4, 7); + GPIO_EMC_08(Gpio4, 8); + GPIO_EMC_09(Gpio4, 9); + GPIO_EMC_10(Gpio4, 10); + GPIO_EMC_11(Gpio4, 11); + GPIO_EMC_12(Gpio4, 12); + GPIO_EMC_13(Gpio4, 13); + GPIO_EMC_14(Gpio4, 14); + GPIO_EMC_15(Gpio4, 15); + GPIO_EMC_16(Gpio4, 16); + GPIO_EMC_17(Gpio4, 17); + GPIO_EMC_18(Gpio4, 18); + GPIO_EMC_19(Gpio4, 19); + GPIO_EMC_20(Gpio4, 20); + GPIO_EMC_21(Gpio4, 21); + GPIO_EMC_22(Gpio4, 22); + GPIO_EMC_23(Gpio4, 23); + GPIO_EMC_24(Gpio4, 24); + GPIO_EMC_25(Gpio4, 25); + GPIO_EMC_26(Gpio4, 26); + GPIO_EMC_27(Gpio4, 27); + GPIO_EMC_28(Gpio4, 28); + GPIO_EMC_29(Gpio4, 29); + GPIO_EMC_30(Gpio4, 30); + GPIO_EMC_31(Gpio4, 31); + + // GPIO Bank 3 + GPIO_EMC_32(Gpio3, 18); + GPIO_EMC_33(Gpio3, 19); + GPIO_EMC_34(Gpio3, 20); + GPIO_EMC_35(Gpio3, 21); + GPIO_EMC_36(Gpio3, 22); + GPIO_EMC_37(Gpio3, 23); + GPIO_EMC_38(Gpio3, 24); + GPIO_EMC_39(Gpio3, 25); + GPIO_EMC_40(Gpio3, 26); + GPIO_EMC_41(Gpio3, 27); + GPIO_SD_B0_00(Gpio3, 12); + GPIO_SD_B0_01(Gpio3, 13); + GPIO_SD_B0_02(Gpio3, 14); + GPIO_SD_B0_03(Gpio3, 15); + GPIO_SD_B0_04(Gpio3, 16); + GPIO_SD_B0_05(Gpio3, 17); + GPIO_SD_B1_00(Gpio3, 0); + GPIO_SD_B1_01(Gpio3, 1); + GPIO_SD_B1_02(Gpio3, 2); + GPIO_SD_B1_03(Gpio3, 3); + GPIO_SD_B1_04(Gpio3, 4); + GPIO_SD_B1_05(Gpio3, 5); + GPIO_SD_B1_06(Gpio3, 6); + GPIO_SD_B1_07(Gpio3, 7); + GPIO_SD_B1_08(Gpio3, 8); + GPIO_SD_B1_09(Gpio3, 9); + GPIO_SD_B1_10(Gpio3, 10); + GPIO_SD_B1_11(Gpio3, 11); + + WAKEUP(Gpio5, 0); + PMIC_ON_REQ(Gpio5, 1); + PMIC_STBY_REQ(Gpio5, 2); +} + +pub(crate) mod _generated { + #![allow(dead_code)] + #![allow(unused_imports)] + #![allow(non_snake_case)] + #![allow(missing_docs)] + + include!(concat!(env!("OUT_DIR"), "/_generated.rs")); +} diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs index 9c58e8a7d..1d60a0d51 100644 --- a/embassy-nxp/src/gpio/rt1xxx.rs +++ b/embassy-nxp/src/gpio/rt1xxx.rs @@ -12,11 +12,11 @@ use nxp_pac::iomuxc::vals::Pus; use crate::chip::{mux_address, pad_address}; use crate::pac::common::{Reg, RW}; +use crate::pac::gpio::Gpio; #[cfg(feature = "rt")] use crate::pac::interrupt; use crate::pac::iomuxc::regs::{Ctl, MuxCtl}; -#[cfg(gpio5)] -use crate::pac::{self, gpio::Gpio}; +use crate::pac::{self}; /// The GPIO pin level for pins set on "Digital" mode. #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -110,6 +110,14 @@ pub enum Bank { #[cfg(gpio2)] Gpio2, + /// Bank 3 + #[cfg(gpio3)] + Gpio3, + + /// Bank 4 + #[cfg(gpio4)] + Gpio4, + /// Bank 5 #[cfg(gpio5)] Gpio5, @@ -642,6 +650,10 @@ const GPIO_MUX_MODE: u8 = 0b101; static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; #[cfg(gpio2)] static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio3)] +static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio4)] +static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; #[cfg(gpio5)] static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; @@ -658,6 +670,10 @@ pub(crate) trait SealedPin: Sized { Bank::Gpio1 => pac::GPIO1, #[cfg(gpio2)] Bank::Gpio2 => pac::GPIO2, + #[cfg(gpio3)] + Bank::Gpio3 => pac::GPIO3, + #[cfg(gpio4)] + Bank::Gpio4 => pac::GPIO4, #[cfg(gpio5)] Bank::Gpio5 => pac::GPIO5, } @@ -687,6 +703,10 @@ pub(crate) trait SealedPin: Sized { Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize], #[cfg(gpio2)] Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize], + #[cfg(gpio3)] + Bank::Gpio3 => &GPIO3_WAKERS[self.pin_number() as usize], + #[cfg(gpio4)] + Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], #[cfg(gpio5)] Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], } @@ -870,25 +890,55 @@ fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) { } } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO1_COMBINED_0_15() { irq_handler(pac::GPIO1, &GPIO1_WAKERS, false); } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO1_COMBINED_16_31() { irq_handler(pac::GPIO1, &GPIO1_WAKERS, true); } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO2_COMBINED_0_15() { irq_handler(pac::GPIO2, &GPIO2_WAKERS, false); } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO2_COMBINED_16_31() { + irq_handler(pac::GPIO2, &GPIO2_WAKERS, true); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO3_COMBINED_0_15() { + irq_handler(pac::GPIO3, &GPIO3_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO3_COMBINED_16_31() { + irq_handler(pac::GPIO3, &GPIO3_WAKERS, true); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO4_COMBINED_0_15() { + irq_handler(pac::GPIO4, &GPIO4_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO4_COMBINED_16_31() { + irq_handler(pac::GPIO4, &GPIO4_WAKERS, true); +} + +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO5_COMBINED_0_15() { irq_handler(pac::GPIO5, &GPIO5_WAKERS, false); diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index a715770c4..5e77fc0db 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -14,6 +14,7 @@ mod time_driver; // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] #[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] +#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] mod chip; #[cfg(feature = "unstable-pac")] diff --git a/examples/mimxrt1062-evk/.cargo/config.toml b/examples/mimxrt1062-evk/.cargo/config.toml new file mode 100644 index 000000000..ca4c606dc --- /dev/null +++ b/examples/mimxrt1062-evk/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip MIMXRT1060' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M7 + +[env] +DEFMT_LOG = "trace" diff --git a/examples/mimxrt1062-evk/Cargo.toml b/examples/mimxrt1062-evk/Cargo.toml new file mode 100644 index 000000000..430a26b41 --- /dev/null +++ b/examples/mimxrt1062-evk/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "embassy-imxrt1062-evk-examples" +version = "0.1.0" +edition = "2021" +license = "MIT or Apache-2.0" + +[dependencies] +cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.3" +defmt = "1.0.1" +defmt-rtt = "1.0.0" + +embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } +embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1062", "unstable-pac", "time-driver-pit"] } +embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime" +embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = "1.0.0" + +imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1060"] } +panic-probe = { version = "1.0.0", features = ["print-defmt"] } +panic-semihosting = "0.6.0" + +[build-dependencies] +imxrt-rt = { version = "0.1.7", features = ["device"] } + +[profile.release] +debug = 2 diff --git a/examples/mimxrt1062-evk/build.rs b/examples/mimxrt1062-evk/build.rs new file mode 100644 index 000000000..e0e0d547e --- /dev/null +++ b/examples/mimxrt1062-evk/build.rs @@ -0,0 +1,12 @@ +use imxrt_rt::{Family, RuntimeBuilder}; + +fn main() { + RuntimeBuilder::from_flexspi(Family::Imxrt1060, 8 * 1024 * 1024) + .build() + .unwrap(); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + // Not link.x, as imxrt-rt needs to do some special things + println!("cargo:rustc-link-arg-bins=-Timxrt-link.x"); +} diff --git a/examples/mimxrt1062-evk/src/bin/blinky.rs b/examples/mimxrt1062-evk/src/bin/blinky.rs new file mode 100644 index 000000000..b6d90d94d --- /dev/null +++ b/examples/mimxrt1062-evk/src/bin/blinky.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nxp::gpio::{Level, Output}; +use embassy_time::Timer; +// Must include `embassy_imxrt1062_evk_examples` to ensure the FCB gets linked. +use {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_nxp::init(Default::default()); + info!("Hello world!"); + + let led = p.GPIO_AD_B0_08; + let mut led = Output::new(led, Level::Low); + + loop { + Timer::after_millis(500).await; + + info!("Toggle"); + led.toggle(); + } +} diff --git a/examples/mimxrt1062-evk/src/bin/button.rs b/examples/mimxrt1062-evk/src/bin/button.rs new file mode 100644 index 000000000..d60fa3dac --- /dev/null +++ b/examples/mimxrt1062-evk/src/bin/button.rs @@ -0,0 +1,36 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nxp::gpio::{Input, Level, Output, Pull}; +use {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_nxp::init(Default::default()); + info!("Hello world!"); + + // User LED (D8) + let led = p.GPIO_AD_B0_08; + // User button (SW5) + let button = p.WAKEUP; + let mut button = Input::new(button, Pull::Up100K); + let mut led = Output::new(led, Level::Low); + led.set_high(); + + loop { + button.wait_for_falling_edge().await; + + info!("Toggled"); + led.toggle(); + + // Software debounce. + button.wait_for_rising_edge().await; + + // Stabilization. + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + } +} diff --git a/examples/mimxrt1062-evk/src/lib.rs b/examples/mimxrt1062-evk/src/lib.rs new file mode 100644 index 000000000..3f99f9db3 --- /dev/null +++ b/examples/mimxrt1062-evk/src/lib.rs @@ -0,0 +1,60 @@ +//! FlexSPI configuration block (FCB) for the iMXRT1060-EVK +//! +//! This uses IS25WP QuadSPI flash. + +#![no_std] + +use imxrt_boot_gen::flexspi::opcodes::sdr::*; +use imxrt_boot_gen::flexspi::{self, FlashPadType, ReadSampleClockSource, SerialClockFrequency, SerialFlashRegion, *}; +use imxrt_boot_gen::serial_flash::*; +pub use nor::ConfigurationBlock; + +const SEQ_READ: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0xEB)) + .instr(Instr::new(RADDR, Pads::Four, 0x18)) + .instr(Instr::new(DUMMY, Pads::Four, 0x06)) + .instr(Instr::new(READ, Pads::Four, 0x04)) + .build(); +const SEQ_READ_STATUS: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0x05)) + .instr(Instr::new(READ, Pads::One, 0x04)) + .build(); +const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build(); +const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0x20)) + .instr(Instr::new(RADDR, Pads::One, 0x18)) + .build(); +const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new() + .instr(Instr::new(CMD, Pads::One, 0x02)) + .instr(Instr::new(RADDR, Pads::One, 0x18)) + .instr(Instr::new(WRITE, Pads::One, 0x04)) + .build(); +const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build(); + +const LUT: LookupTable = LookupTable::new() + .command(Command::Read, SEQ_READ) + .command(Command::ReadStatus, SEQ_READ_STATUS) + .command(Command::WriteEnable, SEQ_WRITE_ENABLE) + .command(Command::EraseSector, SEQ_ERASE_SECTOR) + .command(Command::PageProgram, SEQ_PAGE_PROGRAM) + .command(Command::ChipErase, SEQ_CHIP_ERASE); + +const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT) + .version(Version::new(1, 4, 0)) + .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad) + .cs_hold_time(3) + .cs_setup_time(3) + .controller_misc_options(0x10) + .serial_flash_pad_type(FlashPadType::Quad) + .serial_clk_freq(SerialClockFrequency::MHz133) + .flash_size(SerialFlashRegion::A1, 8 * 1024 * 1024); + +pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = + nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK) + .page_size(256) + .sector_size(4096) + .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30); + +#[no_mangle] +#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")] +pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK; -- cgit From a1867f0d742f597a25384e4a33209beeec7ec676 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Sun, 6 Jul 2025 17:35:19 -0500 Subject: mspm0: add buffered uart driver And tests for G3507. --- embassy-mspm0/Cargo.toml | 4 + embassy-mspm0/src/uart.rs | 1111 -------------------------------- embassy-mspm0/src/uart/buffered.rs | 1060 ++++++++++++++++++++++++++++++ embassy-mspm0/src/uart/mod.rs | 1174 ++++++++++++++++++++++++++++++++++ examples/mspm0g3507/Cargo.toml | 2 + tests/mspm0/Cargo.toml | 3 + tests/mspm0/src/bin/uart_buffered.rs | 115 ++++ 7 files changed, 2358 insertions(+), 1111 deletions(-) delete mode 100644 embassy-mspm0/src/uart.rs create mode 100644 embassy-mspm0/src/uart/buffered.rs create mode 100644 embassy-mspm0/src/uart/mod.rs create mode 100644 tests/mspm0/src/bin/uart_buffered.rs diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index d2adc63d7..61afcd76d 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -36,7 +36,10 @@ embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", de embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } embedded-hal = { version = "1.0" } +embedded-hal-nb = { version = "1.0" } embedded-hal-async = { version = "1.0" } +embedded-io = "0.6.1" +embedded-io-async = "0.6.1" defmt = { version = "1.0.1", optional = true } fixed = "1.29" @@ -51,6 +54,7 @@ mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag [build-dependencies] proc-macro2 = "1.0.94" quote = "1.0.40" +cfg_aliases = "0.2.1" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f", default-features = false, features = ["metadata"] } diff --git a/embassy-mspm0/src/uart.rs b/embassy-mspm0/src/uart.rs deleted file mode 100644 index bc1d2e343..000000000 --- a/embassy-mspm0/src/uart.rs +++ /dev/null @@ -1,1111 +0,0 @@ -#![macro_use] - -use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; - -use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::PeripheralType; - -use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; -use crate::interrupt::{Interrupt, InterruptExt}; -use crate::mode::{Blocking, Mode}; -use crate::pac::uart::{vals, Uart as Regs}; -use crate::Peri; - -/// The clock source for the UART. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ClockSel { - /// Use the low frequency clock. - /// - /// The LFCLK runs at 32.768 kHz. - LfClk, - - /// Use the middle frequency clock. - /// - /// The MCLK runs at 4 MHz. - MfClk, - // BusClk, - // BusClk depends on the timer's power domain. - // This will be implemented later. -} - -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// The order of bits in byte. -pub enum BitOrder { - /// The most significant bit is first. - MsbFirst, - - /// The least significant bit is first. - LsbFirst, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Number of data bits -pub enum DataBits { - /// 5 Data Bits - DataBits5, - - /// 6 Data Bits - DataBits6, - - /// 7 Data Bits - DataBits7, - - /// 8 Data Bits - DataBits8, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Parity -pub enum Parity { - /// No parity - ParityNone, - - /// Even Parity - ParityEven, - - /// Odd Parity - ParityOdd, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Number of stop bits -pub enum StopBits { - /// One stop bit - Stop1, - - /// Two stop bits - Stop2, -} - -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Config Error -pub enum ConfigError { - /// Rx or Tx not enabled - RxOrTxNotEnabled, - - /// The baud rate could not be configured with the given clocks. - InvalidBaudRate, -} - -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -/// Config -pub struct Config { - /// UART clock source. - pub clock_source: ClockSel, - - /// Baud rate - pub baudrate: u32, - - /// Number of data bits. - pub data_bits: DataBits, - - /// Number of stop bits. - pub stop_bits: StopBits, - - /// Parity type. - pub parity: Parity, - - /// The order of bits in a transmitted/received byte. - pub msb_order: BitOrder, - - /// If true: the `TX` is internally connected to `RX`. - pub loop_back_enable: bool, - - // TODO: Pending way to check if uart is extended - // /// If true: [manchester coding] is used. - // /// - // /// [manchester coding]: https://en.wikipedia.org/wiki/Manchester_code - // pub manchester: bool, - - // TODO: majority voting - - // TODO: fifo level select - need power domain info in metapac - - // TODO: glitch suppression - /// If true: invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_tx: bool, - - /// If true: invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_rx: bool, - - /// If true: invert RTS pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_rts: bool, - - /// If true: invert CTS pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_cts: bool, - - /// Set the pull configuration for the TX pin. - pub tx_pull: Pull, - - /// Set the pull configuration for the RX pin. - pub rx_pull: Pull, - - /// Set the pull configuration for the RTS pin. - pub rts_pull: Pull, - - /// Set the pull configuration for the CTS pin. - pub cts_pull: Pull, -} - -impl Default for Config { - fn default() -> Self { - Self { - clock_source: ClockSel::MfClk, - baudrate: 115200, - data_bits: DataBits::DataBits8, - stop_bits: StopBits::Stop1, - parity: Parity::ParityNone, - // hardware default - msb_order: BitOrder::LsbFirst, - loop_back_enable: false, - // manchester: false, - invert_tx: false, - invert_rx: false, - invert_rts: false, - invert_cts: false, - tx_pull: Pull::None, - rx_pull: Pull::None, - rts_pull: Pull::None, - cts_pull: Pull::None, - } - } -} - -/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`]. -/// -/// ### Notes on [`embedded_io::Read`] -/// -/// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide. -/// -/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] -/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. -pub struct Uart<'d, M: Mode> { - tx: UartTx<'d, M>, - rx: UartRx<'d, M>, -} - -impl<'d, M: Mode> SetConfig for Uart<'d, M> { - type Config = Config; - type ConfigError = ConfigError; - - fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - self.tx.set_config(config)?; - self.rx.set_config(config) - } -} - -/// Serial error -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - Framing, - - Noise, - - Overrun, - - Parity, - - Break, -} - -impl core::fmt::Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let message = match self { - Self::Framing => "Framing Error", - Self::Noise => "Noise Error", - Self::Overrun => "RX Buffer Overrun", - Self::Parity => "Parity Check Error", - Self::Break => "Break Error", - }; - - write!(f, "{}", message) - } -} - -impl core::error::Error for Error {} - -/// Rx-only UART Driver. -/// -/// Can be obtained from [`Uart::split`], or can be constructed independently, -/// if you do not need the transmitting half of the driver. -pub struct UartRx<'d, M: Mode> { - info: &'static Info, - state: &'static State, - rx: Option>, - rts: Option>, - _phantom: PhantomData, -} - -impl<'d, M: Mode> SetConfig for UartRx<'d, M> { - type Config = Config; - type ConfigError = ConfigError; - - fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - self.set_config(config) - } -} - -impl<'d> UartRx<'d, Blocking> { - /// Create a new rx-only UART with no hardware flow control. - /// - /// Useful if you only want Uart Rx. It saves 1 pin . - pub fn new_blocking( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - config: Config, - ) -> Result { - Self::new_inner(peri, new_pin!(rx, config.rx_pf()), None, config) - } - - /// Create a new rx-only UART with a request-to-send pin - pub fn new_blocking_with_rts( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - rts: Peri<'d, impl RtsPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_pf()), - new_pin!(rts, config.rts_pf()), - config, - ) - } -} - -impl<'d, M: Mode> UartRx<'d, M> { - /// Reconfigure the driver - pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - if let Some(ref rx) = self.rx { - rx.update_pf(config.rx_pf()); - } - - if let Some(ref rts) = self.rts { - rts.update_pf(config.rts_pf()); - } - - reconfigure(self.info, self.state, config) - } - - /// Perform a blocking read into `buffer` - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let r = self.info.regs; - - for b in buffer { - // Wait if nothing has arrived yet. - while r.stat().read().rxfe() {} - - // Prevent the compiler from reading from buffer too early - compiler_fence(Ordering::Acquire); - *b = read_with_error(r)?; - } - - Ok(()) - } - - /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { - set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) - } -} - -impl<'d, M: Mode> Drop for UartRx<'d, M> { - fn drop(&mut self) { - self.rx.as_ref().map(|x| x.set_as_disconnected()); - self.rts.as_ref().map(|x| x.set_as_disconnected()); - } -} - -/// Tx-only UART Driver. -/// -/// Can be obtained from [`Uart::split`], or can be constructed independently, -/// if you do not need the receiving half of the driver. -pub struct UartTx<'d, M: Mode> { - info: &'static Info, - state: &'static State, - tx: Option>, - cts: Option>, - _phantom: PhantomData, -} - -impl<'d, M: Mode> SetConfig for UartTx<'d, M> { - type Config = Config; - type ConfigError = ConfigError; - - fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - reconfigure(self.info, self.state, config) - } -} - -impl<'d> UartTx<'d, Blocking> { - /// Create a new blocking tx-only UART with no hardware flow control. - /// - /// Useful if you only want Uart Tx. It saves 1 pin. - pub fn new_blocking( - peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, - config: Config, - ) -> Result { - Self::new_inner(peri, new_pin!(tx, config.tx_pf()), None, config) - } - - /// Create a new blocking tx-only UART with a clear-to-send pin - pub fn new_blocking_with_cts( - peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, - cts: Peri<'d, impl CtsPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(tx, config.tx_pf()), - new_pin!(cts, config.cts_pf()), - config, - ) - } -} - -impl<'d, M: Mode> UartTx<'d, M> { - /// Reconfigure the driver - pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - if let Some(ref tx) = self.tx { - tx.update_pf(config.tx_pf()); - } - - if let Some(ref cts) = self.cts { - cts.update_pf(config.cts_pf()); - } - - reconfigure(self.info, self.state, config) - } - - /// Perform a blocking UART write - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let r = self.info.regs; - - for &b in buffer { - // Wait if there is no space - while !r.stat().read().txfe() {} - - // Prevent the compiler from writing to buffer too early - compiler_fence(Ordering::Release); - r.txdata().write(|w| { - w.set_data(b); - }); - } - - Ok(()) - } - - /// Block until transmission complete - pub fn blocking_flush(&mut self) -> Result<(), Error> { - let r = self.info.regs; - - // Wait until TX fifo/buffer is empty - while r.stat().read().txfe() {} - Ok(()) - } - - /// Send break character - pub fn send_break(&self) { - let r = self.info.regs; - - r.lcrh().modify(|w| { - w.set_brk(true); - }); - } - - /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { - set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) - } -} - -impl<'d, M: Mode> Drop for UartTx<'d, M> { - fn drop(&mut self) { - self.tx.as_ref().map(|x| x.set_as_disconnected()); - self.cts.as_ref().map(|x| x.set_as_disconnected()); - } -} - -impl<'d> Uart<'d, Blocking> { - /// Create a new blocking bidirectional UART. - pub fn new_blocking( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_pf()), - new_pin!(tx, config.tx_pf()), - None, - None, - config, - ) - } - - /// Create a new bidirectional UART with request-to-send and clear-to-send pins - pub fn new_blocking_with_rtscts( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - rts: Peri<'d, impl RtsPin>, - cts: Peri<'d, impl CtsPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_pf()), - new_pin!(tx, config.tx_pf()), - new_pin!(rts, config.rts_pf()), - new_pin!(cts, config.cts_pf()), - config, - ) - } -} - -impl<'d, M: Mode> Uart<'d, M> { - /// Perform a blocking write - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.blocking_write(buffer) - } - - /// Block until transmission complete - pub fn blocking_flush(&mut self) -> Result<(), Error> { - self.tx.blocking_flush() - } - - /// Perform a blocking read into `buffer` - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.blocking_read(buffer) - } - - /// Split the Uart into a transmitter and receiver, which is - /// particularly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { - (self.tx, self.rx) - } - - /// Split the Uart into a transmitter and receiver by mutable reference, - /// which is particularly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { - (&mut self.tx, &mut self.rx) - } - - /// Send break character - pub fn send_break(&self) { - self.tx.send_break(); - } - - /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { - set_baudrate(&self.tx.info, self.tx.state.clock.load(Ordering::Relaxed), baudrate) - } -} - -/// Peripheral instance trait. -#[allow(private_bounds)] -pub trait Instance: SealedInstance + PeripheralType { - type Interrupt: crate::interrupt::typelevel::Interrupt; -} - -/// UART `TX` pin trait -pub trait TxPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `TX`. - fn pf_num(&self) -> u8; -} - -/// UART `RX` pin trait -pub trait RxPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `RX`. - fn pf_num(&self) -> u8; -} - -/// UART `CTS` pin trait -pub trait CtsPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `CTS`. - fn pf_num(&self) -> u8; -} - -/// UART `RTS` pin trait -pub trait RtsPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `RTS`. - fn pf_num(&self) -> u8; -} - -// ==== IMPL types ==== - -pub(crate) struct Info { - pub(crate) regs: Regs, - pub(crate) interrupt: Interrupt, -} - -pub(crate) struct State { - /// The clock rate of the UART. This might be configured. - pub(crate) clock: AtomicU32, -} - -impl<'d, M: Mode> UartRx<'d, M> { - fn new_inner( - _peri: Peri<'d, T>, - rx: Option>, - rts: Option>, - config: Config, - ) -> Result { - let mut this = Self { - info: T::info(), - state: T::state(), - rx, - rts, - _phantom: PhantomData, - }; - this.enable_and_configure(&config)?; - - Ok(this) - } - - fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { - let info = self.info; - - enable(info.regs); - configure(info, self.state, config, true, self.rts.is_some(), false, false)?; - - Ok(()) - } -} - -impl<'d, M: Mode> UartTx<'d, M> { - fn new_inner( - _peri: Peri<'d, T>, - tx: Option>, - cts: Option>, - config: Config, - ) -> Result { - let mut this = Self { - info: T::info(), - state: T::state(), - tx, - cts, - _phantom: PhantomData, - }; - this.enable_and_configure(&config)?; - - Ok(this) - } - - fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { - let info = self.info; - let state = self.state; - - enable(info.regs); - configure(info, state, config, false, false, true, self.cts.is_some())?; - - Ok(()) - } -} - -impl<'d, M: Mode> Uart<'d, M> { - fn new_inner( - _peri: Peri<'d, T>, - rx: Option>, - tx: Option>, - rts: Option>, - cts: Option>, - config: Config, - ) -> Result { - let info = T::info(); - let state = T::state(); - - let mut this = Self { - tx: UartTx { - info, - state, - tx, - cts, - _phantom: PhantomData, - }, - rx: UartRx { - info, - state, - rx, - rts, - _phantom: PhantomData, - }, - }; - this.enable_and_configure(&config)?; - - Ok(this) - } - - fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { - let info = self.rx.info; - let state = self.rx.state; - - enable(info.regs); - configure( - info, - state, - config, - true, - self.rx.rts.is_some(), - true, - self.tx.cts.is_some(), - )?; - - info.interrupt.unpend(); - unsafe { info.interrupt.enable() }; - - Ok(()) - } -} - -impl Config { - fn tx_pf(&self) -> PfType { - PfType::output(self.tx_pull, self.invert_tx) - } - - fn rx_pf(&self) -> PfType { - PfType::input(self.rx_pull, self.invert_rx) - } - - fn rts_pf(&self) -> PfType { - PfType::output(self.rts_pull, self.invert_rts) - } - - fn cts_pf(&self) -> PfType { - PfType::input(self.rts_pull, self.invert_rts) - } -} - -fn enable(regs: Regs) { - let gprcm = regs.gprcm(0); - - gprcm.rstctl().write(|w| { - w.set_resetstkyclr(true); - w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); - }); - - gprcm.pwren().write(|w| { - w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); - }); -} - -fn configure( - info: &Info, - state: &State, - config: &Config, - enable_rx: bool, - enable_rts: bool, - enable_tx: bool, - enable_cts: bool, -) -> Result<(), ConfigError> { - let r = info.regs; - - if !enable_rx && !enable_tx { - return Err(ConfigError::RxOrTxNotEnabled); - } - - // SLAU846B says that clocks should be enabled before disabling the uart. - r.clksel().write(|w| match config.clock_source { - ClockSel::LfClk => { - w.set_lfclk_sel(true); - w.set_mfclk_sel(false); - w.set_busclk_sel(false); - } - ClockSel::MfClk => { - w.set_mfclk_sel(true); - w.set_lfclk_sel(false); - w.set_busclk_sel(false); - } - }); - - let clock = match config.clock_source { - ClockSel::LfClk => 32768, - ClockSel::MfClk => 4_000_000, - }; - - state.clock.store(clock, Ordering::Relaxed); - - info.regs.ctl0().modify(|w| { - w.set_lbe(config.loop_back_enable); - w.set_rxe(enable_rx); - w.set_txe(enable_tx); - // RXD_OUT_EN and TXD_OUT_EN? - w.set_menc(false); - w.set_mode(vals::Mode::UART); - w.set_rtsen(enable_rts); - w.set_ctsen(enable_cts); - // oversampling is set later - // TODO: config - w.set_fen(false); - // TODO: config - w.set_majvote(false); - w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); - }); - - info.regs.lcrh().modify(|w| { - let eps = if matches!(config.parity, Parity::ParityEven) { - vals::Eps::EVEN - } else { - vals::Eps::ODD - }; - - let wlen = match config.data_bits { - DataBits::DataBits5 => vals::Wlen::DATABIT5, - DataBits::DataBits6 => vals::Wlen::DATABIT6, - DataBits::DataBits7 => vals::Wlen::DATABIT7, - DataBits::DataBits8 => vals::Wlen::DATABIT8, - }; - - // Used in LIN mode only - w.set_brk(false); - w.set_pen(config.parity != Parity::ParityNone); - w.set_eps(eps); - w.set_stp2(matches!(config.stop_bits, StopBits::Stop2)); - w.set_wlen(wlen); - // appears to only be used in RS-485 mode. - w.set_sps(false); - // IDLE pattern? - w.set_sendidle(false); - // ignore extdir_setup and extdir_hold, only used in RS-485 mode. - }); - - set_baudrate_inner(info.regs, clock, config.baudrate)?; - - r.ctl0().modify(|w| { - w.set_enable(true); - }); - - Ok(()) -} - -fn reconfigure(info: &Info, state: &State, config: &Config) -> Result<(), ConfigError> { - info.interrupt.disable(); - let r = info.regs; - let ctl0 = r.ctl0().read(); - configure(info, state, config, ctl0.rxe(), ctl0.rtsen(), ctl0.txe(), ctl0.ctsen())?; - - info.interrupt.unpend(); - unsafe { info.interrupt.enable() }; - - Ok(()) -} - -/// Set the baud rate and clock settings. -/// -/// This should be done relatively late during configuration since some clock settings are invalid depending on mode. -fn set_baudrate(info: &Info, clock: u32, baudrate: u32) -> Result<(), ConfigError> { - let r = info.regs; - - info.interrupt.disable(); - - // Programming baud rate requires that the peripheral is disabled - critical_section::with(|_cs| { - r.ctl0().modify(|w| { - w.set_enable(false); - }); - }); - - // Wait for end of transmission per suggestion in SLAU 845 section 18.3.28 - while !r.stat().read().txfe() {} - - set_baudrate_inner(r, clock, baudrate)?; - - critical_section::with(|_cs| { - r.ctl0().modify(|w| { - w.set_enable(true); - }); - }); - - info.interrupt.unpend(); - unsafe { info.interrupt.enable() }; - - Ok(()) -} - -fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), ConfigError> { - // Quoting SLAU846 section 18.2.3.4: - // "When IBRD = 0, FBRD is ignored and no data gets transferred by the UART." - const MIN_IBRD: u16 = 1; - - // FBRD can be 0 - // FBRD is at most a 6-bit number. - const MAX_FBRD: u8 = 2_u8.pow(6); - - const DIVS: [(u8, vals::Clkdiv); 8] = [ - (1, vals::Clkdiv::DIV_BY_1), - (2, vals::Clkdiv::DIV_BY_2), - (3, vals::Clkdiv::DIV_BY_3), - (4, vals::Clkdiv::DIV_BY_4), - (5, vals::Clkdiv::DIV_BY_5), - (6, vals::Clkdiv::DIV_BY_6), - (7, vals::Clkdiv::DIV_BY_7), - (8, vals::Clkdiv::DIV_BY_8), - ]; - - // Quoting from SLAU 846 section 18.2.3.4: - // "Select oversampling by 3 or 8 to achieve higher speed with UARTclk/8 or UARTclk/3. In this case - // the receiver tolerance to clock deviation is reduced." - // - // "Select oversampling by 16 to increase the tolerance of the receiver to clock deviations. The - // maximum speed is limited to UARTclk/16." - // - // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock - // deviation. If no valid BRD value can be found satisifying the highest sample rate, then reduce - // sample rate until valid parameters are found. - const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)]; - - // 3x oversampling is not supported with manchester coding, DALI or IrDA. - let x3_invalid = { - let ctl0 = regs.ctl0().read(); - let irctl = regs.irctl().read(); - - ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren() - }; - let mut found = None; - - 'outer: for &(oversampling, hse_value) in &OVS { - if matches!(hse_value, vals::Hse::OVS3) && x3_invalid { - continue; - } - - // Verify that the selected oversampling does not require a clock faster than what the hardware - // is provided. - let Some(min_clock) = baudrate.checked_mul(oversampling as u32) else { - trace!( - "{}x oversampling would cause overflow for clock: {} Hz", - oversampling, - clock - ); - continue; - }; - - if min_clock > clock { - trace!("{} oversampling is too high for clock: {} Hz", oversampling, clock); - continue; - } - - for &(div, div_value) in &DIVS { - trace!( - "Trying div: {}, oversampling {} for {} baud", - div, - oversampling, - baudrate - ); - - let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else { - trace!("Calculating BRD overflowed: trying another divider"); - continue; - }; - - if ibrd < MIN_IBRD || fbrd > MAX_FBRD { - trace!("BRD was invalid: trying another divider"); - continue; - } - - found = Some((hse_value, div_value, ibrd, fbrd)); - break 'outer; - } - } - - let Some((hse, div, ibrd, fbrd)) = found else { - return Err(ConfigError::InvalidBaudRate); - }; - - regs.clkdiv().write(|w| { - w.set_ratio(div); - }); - - regs.ibrd().write(|w| { - w.set_divint(ibrd); - }); - - regs.fbrd().write(|w| { - w.set_divfrac(fbrd); - }); - - regs.ctl0().modify(|w| { - w.set_hse(hse); - }); - - Ok(()) -} - -/// Calculate the integer and fractional parts of the `BRD` value. -/// -/// Returns [`None`] if calculating this results in overflows. -/// -/// Values returned are `(ibrd, fbrd)` -fn calculate_brd(clock: u32, div: u8, baud: u32, oversampling: u8) -> Option<(u16, u8)> { - use fixed::types::U26F6; - - // Calculate BRD according to SLAU 846 section 18.2.3.4. - // - // BRD is a 22-bit value with 16 integer bits and 6 fractional bits. - // - // uart_clock = clock / div - // brd = ibrd.fbrd = uart_clock / (oversampling * baud)" - // - // It is tempting to rearrange the equation such that there is only a single division in - // order to reduce error. However this is wrong since the denominator ends up being too - // small to represent in 6 fraction bits. This means that FBRD would always be 0. - // - // Calculations are done in a U16F6 format. However the fixed crate has no such representation. - // U26F6 is used since it has the same number of fractional bits and we verify at the end that - // the integer part did not overflow. - let clock = U26F6::from_num(clock); - let div = U26F6::from_num(div); - let oversampling = U26F6::from_num(oversampling); - let baud = U26F6::from_num(baud); - - let uart_clock = clock.checked_div(div)?; - - // oversampling * baud - let denom = oversampling.checked_mul(baud)?; - // uart_clock / (oversampling * baud) - let brd = uart_clock.checked_div(denom)?; - - // Checked is used to determine overflow in the 10 most singificant bits since the - // actual representation of BRD is U16F6. - let ibrd = brd.checked_to_num::()?; - - // We need to scale FBRD's representation to an integer. - let fbrd_scale = U26F6::from_num(2_u32.checked_pow(U26F6::FRAC_NBITS)?); - - // It is suggested that 0.5 is added to ensure that any fractional parts round up to the next - // integer. If it doesn't round up then it'll get discarded which is okay. - let half = U26F6::from_num(1) / U26F6::from_num(2); - // fbrd = INT(((FRAC(BRD) * 64) + 0.5)) - let fbrd = brd - .frac() - .checked_mul(fbrd_scale)? - .checked_add(half)? - .checked_to_num::()?; - - Some((ibrd, fbrd)) -} - -fn read_with_error(r: Regs) -> Result { - let rx = r.rxdata().read(); - - if rx.frmerr() { - return Err(Error::Framing); - } else if rx.parerr() { - return Err(Error::Parity); - } else if rx.brkerr() { - return Err(Error::Break); - } else if rx.ovrerr() { - return Err(Error::Overrun); - } else if rx.nerr() { - return Err(Error::Noise); - } - - Ok(rx.data()) -} - -pub(crate) trait SealedInstance { - fn info() -> &'static Info; - fn state() -> &'static State; -} - -macro_rules! impl_uart_instance { - ($instance: ident) => { - impl crate::uart::SealedInstance for crate::peripherals::$instance { - fn info() -> &'static crate::uart::Info { - use crate::interrupt::typelevel::Interrupt; - use crate::uart::Info; - - const INFO: Info = Info { - regs: crate::pac::$instance, - interrupt: crate::interrupt::typelevel::$instance::IRQ, - }; - &INFO - } - - fn state() -> &'static crate::uart::State { - use crate::interrupt::typelevel::Interrupt; - use crate::uart::State; - - static STATE: State = State { - clock: core::sync::atomic::AtomicU32::new(0), - }; - &STATE - } - } - - impl crate::uart::Instance for crate::peripherals::$instance { - type Interrupt = crate::interrupt::typelevel::$instance; - } - }; -} - -macro_rules! impl_uart_tx_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::TxPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -macro_rules! impl_uart_rx_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::RxPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -macro_rules! impl_uart_cts_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::CtsPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -macro_rules! impl_uart_rts_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::RtsPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -#[cfg(test)] -mod tests { - use super::calculate_brd; - - /// This is a smoke test based on the example in SLAU 846 section 18.2.3.4. - #[test] - fn datasheet() { - let brd = calculate_brd(40_000_000, 1, 19200, 16); - - assert!(matches!(brd, Some((130, 13)))); - } -} diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs new file mode 100644 index 000000000..cbc0b6c80 --- /dev/null +++ b/embassy-mspm0/src/uart/buffered.rs @@ -0,0 +1,1060 @@ +use core::future::{poll_fn, Future}; +use core::marker::PhantomData; +use core::slice; +use core::sync::atomic::{AtomicU8, Ordering}; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal_nb::nb; + +use crate::gpio::{AnyPin, SealedPin}; +use crate::interrupt::typelevel::Binding; +use crate::pac::uart::Uart as Regs; +use crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin}; +use crate::{interrupt, Peri}; + +/// Interrupt handler. +pub struct BufferedInterruptHandler { + _uart: PhantomData, +} + +impl interrupt::typelevel::Handler for BufferedInterruptHandler { + unsafe fn on_interrupt() { + on_interrupt(T::info().regs, T::buffered_state()) + } +} + +/// Bidirectional buffered UART which acts as a combination of [`BufferedUartTx`] and [`BufferedUartRx`]. +pub struct BufferedUart<'d> { + rx: BufferedUartRx<'d>, + tx: BufferedUartTx<'d>, +} + +impl SetConfig for BufferedUart<'_> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> BufferedUart<'d> { + /// Create a new bidirectional buffered UART. + pub fn new( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + rx: Peri<'d, impl RxPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + None, + None, + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART with request-to-send and clear-to-send pins + pub fn new_with_rtscts( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + rx: Peri<'d, impl RxPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, impl CtsPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + new_pin!(rts, config.rts_pf()), + new_pin!(cts, config.cts_pf()), + tx_buffer, + rx_buffer, + config, + ) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.tx.set_config(config)?; + self.rx.set_config(config) + } + + /// Set baudrate + pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { + self.rx.set_baudrate(baudrate) + } + + /// Write to UART TX buffer, blocking execution until done. + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { + self.tx.blocking_write(buffer) + } + + /// Flush UART TX buffer, blocking execution until done. + pub fn blocking_flush(&mut self) -> Result<(), Error> { + self.tx.blocking_flush() + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + self.tx.busy() + } + + /// Read from UART RX buffer, blocking execution until done. + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result { + self.rx.blocking_read(buffer) + } + + /// Send break character. + pub fn send_break(&mut self) { + self.tx.send_break() + } + + /// Split into separate RX and TX handles. + pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) { + (self.tx, self.rx) + } + + /// Split into separate RX and TX handles. + pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) { + ( + BufferedUartTx { + info: self.tx.info, + state: self.tx.state, + tx: self.tx.tx.as_mut().map(Peri::reborrow), + cts: self.tx.cts.as_mut().map(Peri::reborrow), + reborrowed: true, + }, + BufferedUartRx { + info: self.rx.info, + state: self.rx.state, + rx: self.rx.rx.as_mut().map(Peri::reborrow), + rts: self.rx.rts.as_mut().map(Peri::reborrow), + reborrowed: true, + }, + ) + } +} + +/// Rx-only buffered UART. +/// +/// Can be obtained from [`BufferedUart::split`], or can be constructed independently, +/// if you do not need the transmitting half of the driver. +pub struct BufferedUartRx<'d> { + info: &'static Info, + state: &'static BufferedState, + rx: Option>, + rts: Option>, + reborrowed: bool, +} + +impl SetConfig for BufferedUartRx<'_> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> BufferedUartRx<'d> { + /// Create a new rx-only buffered UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin. + pub fn new( + uart: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + _irq: impl Binding>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner(uart, new_pin!(rx, config.rx_pf()), None, rx_buffer, config) + } + + /// Create a new rx-only buffered UART with a request-to-send pin + pub fn new_with_rts( + uart: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + rts: Peri<'d, impl RtsPin>, + _irq: impl Binding>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(rx, config.rx_pf()), + new_pin!(rts, config.rts_pf()), + rx_buffer, + config, + ) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref rx) = self.rx { + rx.update_pf(config.rx_pf()); + } + + if let Some(ref rts) = self.rts { + rts.update_pf(config.rts_pf()); + } + + super::reconfigure(&self.info, &self.state.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { + super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) + } + + /// Read from UART RX buffer, blocking execution until done. + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result { + self.blocking_read_inner(buffer) + } +} + +impl Drop for BufferedUartRx<'_> { + fn drop(&mut self) { + if !self.reborrowed { + let state = self.state; + + // SAFETY: RX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized + // in order to meet the requirements of init. + unsafe { + state.rx_buf.deinit(); + } + + // TX is inactive if the buffer is not available. If this is true, then disable the + // interrupt handler since we are running in RX only mode. + if state.tx_buf.len() == 0 { + self.info.interrupt.disable(); + } + + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); + } + } +} + +/// Tx-only buffered UART. +/// +/// Can be obtained from [`BufferedUart::split`], or can be constructed independently, +/// if you do not need the receiving half of the driver. +pub struct BufferedUartTx<'d> { + info: &'static Info, + state: &'static BufferedState, + tx: Option>, + cts: Option>, + reborrowed: bool, +} + +impl SetConfig for BufferedUartTx<'_> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> BufferedUartTx<'d> { + /// Create a new tx-only buffered UART with no hardware flow control. + /// + /// Useful if you only want Uart Tx. It saves 1 pin. + pub fn new( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner(uart, new_pin!(tx, config.tx_pf()), None, tx_buffer, config) + } + + /// Create a new tx-only buffered UART with a clear-to-send pin + pub fn new_with_rts( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + cts: Peri<'d, impl CtsPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(tx, config.tx_pf()), + new_pin!(cts, config.cts_pf()), + tx_buffer, + config, + ) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref tx) = self.tx { + tx.update_pf(config.tx_pf()); + } + + if let Some(ref cts) = self.cts { + cts.update_pf(config.cts_pf()); + } + + super::reconfigure(self.info, &self.state.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) + } + + /// Write to UART TX buffer, blocking execution until done. + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { + self.blocking_write_inner(buffer) + } + + /// Flush UART TX buffer, blocking execution until done. + pub fn blocking_flush(&mut self) -> Result<(), Error> { + let state = self.state; + + loop { + if state.tx_buf.is_empty() { + return Ok(()); + } + } + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + super::busy(self.info.regs) + } + + /// Send break character + pub fn send_break(&mut self) { + let r = self.info.regs; + + r.lcrh().modify(|w| { + w.set_brk(true); + }); + } +} + +impl Drop for BufferedUartTx<'_> { + fn drop(&mut self) { + if !self.reborrowed { + let state = self.state; + + // SAFETY: TX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized + // in order to meet the requirements of init. + unsafe { + state.tx_buf.deinit(); + } + + // RX is inactive if the buffer is not available. If this is true, then disable the + // interrupt handler since we are running in TX only mode. + if state.rx_buf.len() == 0 { + self.info.interrupt.disable(); + } + + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + } + } +} + +impl embedded_io_async::ErrorType for BufferedUart<'_> { + type Error = Error; +} + +impl embedded_io_async::ErrorType for BufferedUartRx<'_> { + type Error = Error; +} + +impl embedded_io_async::ErrorType for BufferedUartTx<'_> { + type Error = Error; +} + +impl embedded_io_async::Read for BufferedUart<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf).await + } +} + +impl embedded_io_async::Read for BufferedUartRx<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_inner(buf).await + } +} + +impl embedded_io_async::ReadReady for BufferedUart<'_> { + fn read_ready(&mut self) -> Result { + self.rx.read_ready() + } +} + +impl embedded_io_async::ReadReady for BufferedUartRx<'_> { + fn read_ready(&mut self) -> Result { + self.read_ready_inner() + } +} + +impl embedded_io_async::BufRead for BufferedUart<'_> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.rx.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.rx.consume(amt); + } +} + +impl embedded_io_async::BufRead for BufferedUartRx<'_> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf_inner().await + } + + fn consume(&mut self, amt: usize) { + self.consume_inner(amt); + } +} + +impl embedded_io_async::Write for BufferedUart<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write_inner(buf).await + } +} + +impl embedded_io_async::Write for BufferedUartTx<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_inner(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_inner().await + } +} + +impl embedded_io::Read for BufferedUart<'_> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf) + } +} + +impl embedded_io::Read for BufferedUartRx<'_> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.blocking_read_inner(buf) + } +} + +impl embedded_io::Write for BufferedUart<'_> { + fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.flush() + } +} + +impl embedded_io::Write for BufferedUartTx<'_> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write_inner(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { + match self { + Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + Error::Break => embedded_hal_nb::serial::ErrorKind::Other, + } + } +} + +impl embedded_hal_nb::serial::ErrorType for BufferedUart<'_> { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BufferedUartRx<'_> { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BufferedUartTx<'_> { + type Error = Error; +} + +impl embedded_hal_nb::serial::Read for BufferedUart<'_> { + fn read(&mut self) -> nb::Result { + self.rx.read() + } +} + +impl embedded_hal_nb::serial::Read for BufferedUartRx<'_> { + fn read(&mut self) -> nb::Result { + if self.info.regs.stat().read().rxfe() { + return Err(nb::Error::WouldBlock); + } + + super::read_with_error(self.info.regs).map_err(nb::Error::Other) + } +} + +impl embedded_hal_nb::serial::Write for BufferedUart<'_> { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.tx.write(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.flush() + } +} + +impl embedded_hal_nb::serial::Write for BufferedUartTx<'_> { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[word]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } +} + +// Impl details + +/// Buffered UART state. +pub(crate) struct BufferedState { + /// non-buffered UART state. This is inline in order to avoid [`BufferedUartRx`]/Tx + /// needing to carry around a 2nd static reference and waste another 4 bytes. + state: State, + rx_waker: AtomicWaker, + rx_buf: RingBuffer, + tx_waker: AtomicWaker, + tx_buf: RingBuffer, + rx_error: AtomicU8, +} + +// these must match bits 8..12 in RXDATA, but shifted by 8 to the right +const RXE_NOISE: u8 = 16; +const RXE_OVERRUN: u8 = 8; +const RXE_BREAK: u8 = 4; +const RXE_PARITY: u8 = 2; +const RXE_FRAMING: u8 = 1; + +impl BufferedState { + pub const fn new() -> Self { + Self { + state: State::new(), + rx_waker: AtomicWaker::new(), + rx_buf: RingBuffer::new(), + tx_waker: AtomicWaker::new(), + tx_buf: RingBuffer::new(), + rx_error: AtomicU8::new(0), + } + } +} + +impl<'d> BufferedUart<'d> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let info = T::info(); + let state = T::buffered_state(); + + let mut this = Self { + tx: BufferedUartTx { + info, + state, + tx, + cts, + reborrowed: false, + }, + rx: BufferedUartRx { + info, + state, + rx, + rts, + reborrowed: false, + }, + }; + this.enable_and_configure(tx_buffer, rx_buffer, &config)?; + + Ok(this) + } + + fn enable_and_configure( + &mut self, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: &Config, + ) -> Result<(), ConfigError> { + let info = self.rx.info; + let state = self.rx.state; + + assert!(!tx_buffer.is_empty()); + assert!(!rx_buffer.is_empty()); + + init_buffers(info, state, Some(tx_buffer), Some(rx_buffer)); + super::enable(info.regs); + super::configure( + info, + &state.state, + config, + true, + self.rx.rts.is_some(), + true, + self.tx.cts.is_some(), + )?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } +} + +impl<'d> BufferedUartRx<'d> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + rts: Option>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::buffered_state(), + rx, + rts, + reborrowed: false, + }; + this.enable_and_configure(rx_buffer, &config)?; + + Ok(this) + } + + fn enable_and_configure(&mut self, rx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + init_buffers(info, state, None, Some(rx_buffer)); + super::enable(info.regs); + super::configure(info, &self.state.state, config, true, self.rts.is_some(), false, false)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } + + async fn read_inner(&self, buf: &mut [u8]) -> Result { + poll_fn(move |cx| { + let state = self.state; + + if let Poll::Ready(r) = self.try_read(buf) { + return Poll::Ready(r); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }) + .await + } + + fn blocking_read_inner(&self, buffer: &mut [u8]) -> Result { + loop { + match self.try_read(buffer) { + Poll::Ready(res) => return res, + Poll::Pending => continue, + } + } + } + + fn fill_buf_inner(&self) -> impl Future> { + poll_fn(move |cx| { + let mut rx_reader = unsafe { self.state.rx_buf.reader() }; + let (p, n) = rx_reader.pop_buf(); + let result = if n == 0 { + match Self::get_rx_error(self.state) { + None => { + self.state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + Some(e) => Err(e), + } + } else { + let buf = unsafe { slice::from_raw_parts(p, n) }; + Ok(buf) + }; + + Poll::Ready(result) + }) + } + + fn consume_inner(&self, amt: usize) { + let mut rx_reader = unsafe { self.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 or errors were detected. + self.info.regs.cpu_int(0).imask().modify(|w| { + w.set_rxint(true); + w.set_rtout(true); + }); + } + + /// we are ready to read if there is data in the buffer + fn read_ready_inner(&self) -> Result { + Ok(!self.state.rx_buf.is_empty()) + } + + fn try_read(&self, buf: &mut [u8]) -> Poll> { + let state = self.state; + + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + 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 + }); + + let result = if n == 0 { + match Self::get_rx_error(state) { + None => return Poll::Pending, + Some(e) => Err(e), + } + } else { + Ok(n) + }; + + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full or errors were detected. + self.info.regs.cpu_int(0).imask().modify(|w| { + w.set_rxint(true); + w.set_rtout(true); + }); + + Poll::Ready(result) + } + + fn get_rx_error(state: &BufferedState) -> Option { + // Cortex-M0 has does not support atomic swap, so we must do two operations. + let errs = critical_section::with(|_cs| { + let errs = state.rx_error.load(Ordering::Relaxed); + state.rx_error.store(0, Ordering::Relaxed); + + errs + }); + + if errs & RXE_NOISE != 0 { + Some(Error::Noise) + } else if errs & RXE_OVERRUN != 0 { + Some(Error::Overrun) + } else if errs & RXE_BREAK != 0 { + Some(Error::Break) + } else if errs & RXE_PARITY != 0 { + Some(Error::Parity) + } else if errs & RXE_FRAMING != 0 { + Some(Error::Framing) + } else { + None + } + } +} + +impl<'d> BufferedUartTx<'d> { + fn new_inner( + _peri: Peri<'d, T>, + tx: Option>, + cts: Option>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::buffered_state(), + tx, + cts, + reborrowed: false, + }; + + this.enable_and_configure(tx_buffer, &config)?; + + Ok(this) + } + + async fn write_inner(&self, buf: &[u8]) -> Result { + poll_fn(move |cx| { + let state = self.state; + + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + 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 n == 0 { + state.tx_waker.register(cx.waker()); + 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. + self.info.interrupt.pend(); + Poll::Ready(Ok(n)) + }) + .await + } + + fn blocking_write_inner(&self, buffer: &[u8]) -> Result { + let state = self.state; + + loop { + let empty = state.tx_buf.is_empty(); + + // SAFETY: tx buf must be initialized if BufferedUartTx exists. + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let data = tx_writer.push_slice(); + + if !data.is_empty() { + let n = data.len().min(buffer.len()); + data[..n].copy_from_slice(&buffer[..n]); + tx_writer.push_done(n); + + if empty { + self.info.interrupt.pend(); + } + + return Ok(n); + } + } + } + + async fn flush_inner(&self) -> Result<(), Error> { + poll_fn(move |cx| { + let state = self.state; + + if !state.tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + .await + } + + fn enable_and_configure(&mut self, tx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + init_buffers(info, state, Some(tx_buffer), None); + super::enable(info.regs); + super::configure(info, &state.state, config, false, false, true, self.cts.is_some())?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } +} + +fn init_buffers<'d>( + info: &Info, + state: &BufferedState, + tx_buffer: Option<&'d mut [u8]>, + rx_buffer: Option<&'d mut [u8]>, +) { + if let Some(tx_buffer) = tx_buffer { + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + } + + if let Some(rx_buffer) = rx_buffer { + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + } + + info.regs.cpu_int(0).imask().modify(|w| { + w.set_nerr(true); + w.set_frmerr(true); + w.set_parerr(true); + w.set_brkerr(true); + w.set_ovrerr(true); + }); +} + +fn on_interrupt(r: Regs, state: &'static BufferedState) { + let int = r.cpu_int(0).mis().read(); + + // Per https://github.com/embassy-rs/embassy/pull/1458, both buffered and unbuffered handlers may be bound. + if super::dma_enabled(r) { + return; + } + + // RX + if state.rx_buf.is_available() { + // SAFETY: RX must have been initialized if RXE is set. + let mut rx_writer = unsafe { state.rx_buf.writer() }; + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + let mut error = false; + + for rx_byte in rx_buf { + let stat = r.stat().read(); + + if stat.rxfe() { + break; + } + + let data = r.rxdata().read(); + + if (data.0 >> 8) != 0 { + // Cortex-M0 does not support atomic fetch_or, must do 2 operations. + critical_section::with(|_cs| { + let mut value = state.rx_error.load(Ordering::Relaxed); + value |= (data.0 >> 8) as u8; + state.rx_error.store(value, Ordering::Relaxed); + }); + error = true; + + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + + *rx_byte = data.data(); + n_read += 1; + } + + if n_read > 0 { + rx_writer.push_done(n_read); + state.rx_waker.wake(); + } else if error { + state.rx_waker.wake(); + } + + // Disable any further RX interrupts when the buffer becomes full or + // errors have occurred. This lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if state.rx_buf.is_full() || error { + r.cpu_int(0).imask().modify(|w| { + w.set_rxint(false); + w.set_rtout(false); + }); + } + } + + if int.eot() { + r.cpu_int(0).imask().modify(|w| { + w.set_eot(false); + }); + + r.cpu_int(0).iclr().write(|w| { + w.set_eot(true); + }); + + state.tx_waker.wake(); + } + + // TX + if state.tx_buf.is_available() { + // SAFETY: TX must have been initialized if TXE is set. + let mut tx_reader = unsafe { state.tx_buf.reader() }; + let buf = tx_reader.pop_slice(); + let mut n_written = 0; + + for tx_byte in buf.iter_mut() { + let stat = r.stat().read(); + + if stat.txff() { + break; + } + + r.txdata().write(|w| { + w.set_data(*tx_byte); + }); + n_written += 1; + } + + if n_written > 0 { + // EOT will wake. + r.cpu_int(0).imask().modify(|w| { + w.set_eot(true); + }); + + tx_reader.pop_done(n_written); + } + } + + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by writing to ICLR. + let mis = r.cpu_int(0).mis().read(); + r.cpu_int(0).iclr().write(|w| { + w.set_nerr(mis.nerr()); + w.set_frmerr(mis.frmerr()); + w.set_parerr(mis.parerr()); + w.set_brkerr(mis.brkerr()); + w.set_ovrerr(mis.ovrerr()); + }); + + // Errors + if mis.nerr() { + warn!("Noise error"); + } + if mis.frmerr() { + warn!("Framing error"); + } + if mis.parerr() { + warn!("Parity error"); + } + if mis.brkerr() { + warn!("Break error"); + } + if mis.ovrerr() { + warn!("Overrun error"); + } +} diff --git a/embassy-mspm0/src/uart/mod.rs b/embassy-mspm0/src/uart/mod.rs new file mode 100644 index 000000000..6599cea06 --- /dev/null +++ b/embassy-mspm0/src/uart/mod.rs @@ -0,0 +1,1174 @@ +#![macro_use] + +mod buffered; + +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; + +pub use buffered::*; +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::PeripheralType; + +use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::mode::{Blocking, Mode}; +use crate::pac::uart::{vals, Uart as Regs}; +use crate::Peri; + +/// The clock source for the UART. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockSel { + /// Use the low frequency clock. + /// + /// The LFCLK runs at 32.768 kHz. + LfClk, + + /// Use the middle frequency clock. + /// + /// The MCLK runs at 4 MHz. + MfClk, + // BusClk, + // BusClk depends on the timer's power domain. + // This will be implemented later. +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The order of bits in byte. +pub enum BitOrder { + /// The most significant bit is first. + MsbFirst, + + /// The least significant bit is first. + LsbFirst, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Number of data bits +pub enum DataBits { + /// 5 Data Bits + DataBits5, + + /// 6 Data Bits + DataBits6, + + /// 7 Data Bits + DataBits7, + + /// 8 Data Bits + DataBits8, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Parity +pub enum Parity { + /// No parity + ParityNone, + + /// Even Parity + ParityEven, + + /// Odd Parity + ParityOdd, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Number of stop bits +pub enum StopBits { + /// One stop bit + Stop1, + + /// Two stop bits + Stop2, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Config Error +pub enum ConfigError { + /// Rx or Tx not enabled + RxOrTxNotEnabled, + + /// The baud rate could not be configured with the given clocks. + InvalidBaudRate, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Config +pub struct Config { + /// UART clock source. + pub clock_source: ClockSel, + + /// Baud rate + pub baudrate: u32, + + /// Number of data bits. + pub data_bits: DataBits, + + /// Number of stop bits. + pub stop_bits: StopBits, + + /// Parity type. + pub parity: Parity, + + /// The order of bits in a transmitted/received byte. + pub msb_order: BitOrder, + + /// If true: the `TX` is internally connected to `RX`. + pub loop_back_enable: bool, + + // TODO: Pending way to check if uart is extended + // /// If true: [manchester coding] is used. + // /// + // /// [manchester coding]: https://en.wikipedia.org/wiki/Manchester_code + // pub manchester: bool, + + // TODO: majority voting + /// If true: the built-in FIFO is enabled. + pub fifo_enable: bool, + + // TODO: glitch suppression + /// If true: invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_tx: bool, + + /// If true: invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_rx: bool, + + /// If true: invert RTS pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_rts: bool, + + /// If true: invert CTS pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_cts: bool, + + /// Set the pull configuration for the TX pin. + pub tx_pull: Pull, + + /// Set the pull configuration for the RX pin. + pub rx_pull: Pull, + + /// Set the pull configuration for the RTS pin. + pub rts_pull: Pull, + + /// Set the pull configuration for the CTS pin. + pub cts_pull: Pull, +} + +impl Default for Config { + fn default() -> Self { + Self { + clock_source: ClockSel::MfClk, + baudrate: 115200, + data_bits: DataBits::DataBits8, + stop_bits: StopBits::Stop1, + parity: Parity::ParityNone, + // hardware default + msb_order: BitOrder::LsbFirst, + loop_back_enable: false, + // manchester: false, + fifo_enable: false, + invert_tx: false, + invert_rx: false, + invert_rts: false, + invert_cts: false, + tx_pull: Pull::None, + rx_pull: Pull::None, + rts_pull: Pull::None, + cts_pull: Pull::None, + } + } +} + +/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`]. +/// +/// ### Notes on [`embedded_io::Read`] +/// +/// [`embedded_io::Read`] requires guarantees that the base [`UartRx`] cannot provide. +/// +/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] +/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. +pub struct Uart<'d, M: Mode> { + tx: UartTx<'d, M>, + rx: UartRx<'d, M>, +} + +impl<'d, M: Mode> SetConfig for Uart<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +/// Serial error +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + Framing, + + Noise, + + Overrun, + + Parity, + + Break, +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let message = match self { + Self::Framing => "Framing Error", + Self::Noise => "Noise Error", + Self::Overrun => "RX Buffer Overrun", + Self::Parity => "Parity Check Error", + Self::Break => "Break Error", + }; + + write!(f, "{}", message) + } +} + +impl core::error::Error for Error {} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +/// Rx-only UART Driver. +/// +/// Can be obtained from [`Uart::split`], or can be constructed independently, +/// if you do not need the transmitting half of the driver. +pub struct UartRx<'d, M: Mode> { + info: &'static Info, + state: &'static State, + rx: Option>, + rts: Option>, + _phantom: PhantomData, +} + +impl<'d, M: Mode> SetConfig for UartRx<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> UartRx<'d, Blocking> { + /// Create a new rx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin. + pub fn new_blocking( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(rx, config.rx_pf()), None, config) + } + + /// Create a new rx-only UART with a request-to-send pin + pub fn new_blocking_with_rts( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + rts: Peri<'d, impl RtsPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_pf()), + new_pin!(rts, config.rts_pf()), + config, + ) + } +} + +impl<'d, M: Mode> UartRx<'d, M> { + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let r = self.info.regs; + + for b in buffer { + // Wait if nothing has arrived yet. + while r.stat().read().rxfe() {} + + // Prevent the compiler from reading from buffer too early + compiler_fence(Ordering::Acquire); + *b = read_with_error(r)?; + } + + Ok(()) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref rx) = self.rx { + rx.update_pf(config.rx_pf()); + } + + if let Some(ref rts) = self.rts { + rts.update_pf(config.rts_pf()); + } + + reconfigure(self.info, self.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) + } +} + +impl<'d, M: Mode> Drop for UartRx<'d, M> { + fn drop(&mut self) { + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); + } +} + +/// Tx-only UART Driver. +/// +/// Can be obtained from [`Uart::split`], or can be constructed independently, +/// if you do not need the receiving half of the driver. +pub struct UartTx<'d, M: Mode> { + info: &'static Info, + state: &'static State, + tx: Option>, + cts: Option>, + _phantom: PhantomData, +} + +impl<'d, M: Mode> SetConfig for UartTx<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + reconfigure(self.info, self.state, config) + } +} + +impl<'d> UartTx<'d, Blocking> { + /// Create a new blocking tx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Tx. It saves 1 pin. + pub fn new_blocking( + peri: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(tx, config.tx_pf()), None, config) + } + + /// Create a new blocking tx-only UART with a clear-to-send pin + pub fn new_blocking_with_cts( + peri: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + cts: Peri<'d, impl CtsPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, config.tx_pf()), + new_pin!(cts, config.cts_pf()), + config, + ) + } +} + +impl<'d, M: Mode> UartTx<'d, M> { + /// Perform a blocking UART write + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let r = self.info.regs; + + for &b in buffer { + // Wait if there is no space + while !r.stat().read().txfe() {} + + // Prevent the compiler from writing to buffer too early + compiler_fence(Ordering::Release); + r.txdata().write(|w| { + w.set_data(b); + }); + } + + Ok(()) + } + + /// Block until transmission complete + pub fn blocking_flush(&mut self) -> Result<(), Error> { + let r = self.info.regs; + + // Wait until TX fifo/buffer is empty + while r.stat().read().txfe() {} + Ok(()) + } + + /// Send break character + pub fn send_break(&self) { + let r = self.info.regs; + + r.lcrh().modify(|w| { + w.set_brk(true); + }); + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + busy(self.info.regs) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref tx) = self.tx { + tx.update_pf(config.tx_pf()); + } + + if let Some(ref cts) = self.cts { + cts.update_pf(config.cts_pf()); + } + + reconfigure(self.info, self.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) + } +} + +impl<'d, M: Mode> Drop for UartTx<'d, M> { + fn drop(&mut self) { + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + } +} + +impl<'d> Uart<'d, Blocking> { + /// Create a new blocking bidirectional UART. + pub fn new_blocking( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + tx: Peri<'d, impl TxPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + None, + None, + config, + ) + } + + /// Create a new bidirectional UART with request-to-send and clear-to-send pins + pub fn new_blocking_with_rtscts( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + tx: Peri<'d, impl TxPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, impl CtsPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + new_pin!(rts, config.rts_pf()), + new_pin!(cts, config.cts_pf()), + config, + ) + } +} + +impl<'d, M: Mode> Uart<'d, M> { + /// Perform a blocking write + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.blocking_write(buffer) + } + + /// Block until transmission complete + pub fn blocking_flush(&mut self) -> Result<(), Error> { + self.tx.blocking_flush() + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + self.tx.busy() + } + + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.blocking_read(buffer) + } + + /// Split the Uart into a transmitter and receiver, which is + /// particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { + (self.tx, self.rx) + } + + /// Split the Uart into a transmitter and receiver by mutable reference, + /// which is particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { + (&mut self.tx, &mut self.rx) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.tx.set_config(config)?; + self.rx.set_config(config) + } + + /// Send break character + pub fn send_break(&self) { + self.tx.send_break(); + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(&self.tx.info, self.tx.state.clock.load(Ordering::Relaxed), baudrate) + } +} + +/// Peripheral instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType { + type Interrupt: crate::interrupt::typelevel::Interrupt; +} + +/// UART `TX` pin trait +pub trait TxPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `TX`. + fn pf_num(&self) -> u8; +} + +/// UART `RX` pin trait +pub trait RxPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `RX`. + fn pf_num(&self) -> u8; +} + +/// UART `CTS` pin trait +pub trait CtsPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `CTS`. + fn pf_num(&self) -> u8; +} + +/// UART `RTS` pin trait +pub trait RtsPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `RTS`. + fn pf_num(&self) -> u8; +} + +// ==== IMPL types ==== + +pub(crate) struct Info { + pub(crate) regs: Regs, + pub(crate) interrupt: Interrupt, +} + +pub(crate) struct State { + /// The clock rate of the UART in Hz. + clock: AtomicU32, +} + +impl State { + pub const fn new() -> Self { + Self { + clock: AtomicU32::new(0), + } + } +} + +impl<'d, M: Mode> UartRx<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + rts: Option>, + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::state(), + rx, + rts, + _phantom: PhantomData, + }; + this.enable_and_configure(&config)?; + + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + + enable(info.regs); + configure(info, self.state, config, true, self.rts.is_some(), false, false)?; + + Ok(()) + } +} + +impl<'d, M: Mode> UartTx<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + tx: Option>, + cts: Option>, + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::state(), + tx, + cts, + _phantom: PhantomData, + }; + this.enable_and_configure(&config)?; + + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + enable(info.regs); + configure(info, state, config, false, false, true, self.cts.is_some())?; + + Ok(()) + } +} + +impl<'d, M: Mode> Uart<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + config: Config, + ) -> Result { + let info = T::info(); + let state = T::state(); + + let mut this = Self { + tx: UartTx { + info, + state, + tx, + cts, + _phantom: PhantomData, + }, + rx: UartRx { + info, + state, + rx, + rts, + _phantom: PhantomData, + }, + }; + this.enable_and_configure(&config)?; + + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.rx.info; + let state = self.rx.state; + + enable(info.regs); + configure( + info, + state, + config, + true, + self.rx.rts.is_some(), + true, + self.tx.cts.is_some(), + )?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } +} + +impl Config { + fn tx_pf(&self) -> PfType { + PfType::output(self.tx_pull, self.invert_tx) + } + + fn rx_pf(&self) -> PfType { + PfType::input(self.rx_pull, self.invert_rx) + } + + fn rts_pf(&self) -> PfType { + PfType::output(self.rts_pull, self.invert_rts) + } + + fn cts_pf(&self) -> PfType { + PfType::input(self.rts_pull, self.invert_rts) + } +} + +fn enable(regs: Regs) { + let gprcm = regs.gprcm(0); + + gprcm.rstctl().write(|w| { + w.set_resetstkyclr(true); + w.set_resetassert(true); + w.set_key(vals::ResetKey::KEY); + }); + + gprcm.pwren().write(|w| { + w.set_enable(true); + w.set_key(vals::PwrenKey::KEY); + }); +} + +fn configure( + info: &Info, + state: &State, + config: &Config, + enable_rx: bool, + enable_rts: bool, + enable_tx: bool, + enable_cts: bool, +) -> Result<(), ConfigError> { + let r = info.regs; + + if !enable_rx && !enable_tx { + return Err(ConfigError::RxOrTxNotEnabled); + } + + // SLAU846B says that clocks should be enabled before disabling the uart. + r.clksel().write(|w| match config.clock_source { + ClockSel::LfClk => { + w.set_lfclk_sel(true); + w.set_mfclk_sel(false); + w.set_busclk_sel(false); + } + ClockSel::MfClk => { + w.set_mfclk_sel(true); + w.set_lfclk_sel(false); + w.set_busclk_sel(false); + } + }); + + let clock = match config.clock_source { + ClockSel::LfClk => 32768, + ClockSel::MfClk => 4_000_000, + }; + + state.clock.store(clock, Ordering::Relaxed); + + info.regs.ctl0().modify(|w| { + w.set_lbe(config.loop_back_enable); + // Errata UART_ERR_02, must set RXE to allow use of EOT. + w.set_rxe(enable_rx | enable_tx); + w.set_txe(enable_tx); + // RXD_OUT_EN and TXD_OUT_EN? + w.set_menc(false); + w.set_mode(vals::Mode::UART); + w.set_rtsen(enable_rts); + w.set_ctsen(enable_cts); + // oversampling is set later + w.set_fen(config.fifo_enable); + // TODO: config + w.set_majvote(false); + w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); + }); + + info.regs.ifls().modify(|w| { + // TODO: Need power domain info for other options. + w.set_txiflsel(vals::Iflssel::AT_LEAST_ONE); + w.set_rxiflsel(vals::Iflssel::AT_LEAST_ONE); + }); + + info.regs.lcrh().modify(|w| { + let eps = if matches!(config.parity, Parity::ParityEven) { + vals::Eps::EVEN + } else { + vals::Eps::ODD + }; + + let wlen = match config.data_bits { + DataBits::DataBits5 => vals::Wlen::DATABIT5, + DataBits::DataBits6 => vals::Wlen::DATABIT6, + DataBits::DataBits7 => vals::Wlen::DATABIT7, + DataBits::DataBits8 => vals::Wlen::DATABIT8, + }; + + // Used in LIN mode only + w.set_brk(false); + w.set_pen(config.parity != Parity::ParityNone); + w.set_eps(eps); + w.set_stp2(matches!(config.stop_bits, StopBits::Stop2)); + w.set_wlen(wlen); + // appears to only be used in RS-485 mode. + w.set_sps(false); + // IDLE pattern? + w.set_sendidle(false); + // ignore extdir_setup and extdir_hold, only used in RS-485 mode. + }); + + set_baudrate_inner(info.regs, clock, config.baudrate)?; + + r.ctl0().modify(|w| { + w.set_enable(true); + }); + + Ok(()) +} + +fn reconfigure(info: &Info, state: &State, config: &Config) -> Result<(), ConfigError> { + info.interrupt.disable(); + let r = info.regs; + let ctl0 = r.ctl0().read(); + configure(info, state, config, ctl0.rxe(), ctl0.rtsen(), ctl0.txe(), ctl0.ctsen())?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +/// Set the baud rate and clock settings. +/// +/// This should be done relatively late during configuration since some clock settings are invalid depending on mode. +fn set_baudrate(info: &Info, clock: u32, baudrate: u32) -> Result<(), ConfigError> { + let r = info.regs; + + info.interrupt.disable(); + + // Programming baud rate requires that the peripheral is disabled + critical_section::with(|_cs| { + r.ctl0().modify(|w| { + w.set_enable(false); + }); + }); + + // Wait for end of transmission per suggestion in SLAU 845 section 18.3.28 + while !r.stat().read().txfe() {} + + set_baudrate_inner(r, clock, baudrate)?; + + critical_section::with(|_cs| { + r.ctl0().modify(|w| { + w.set_enable(true); + }); + }); + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), ConfigError> { + // Quoting SLAU846 section 18.2.3.4: + // "When IBRD = 0, FBRD is ignored and no data gets transferred by the UART." + const MIN_IBRD: u16 = 1; + + // FBRD can be 0 + // FBRD is at most a 6-bit number. + const MAX_FBRD: u8 = 2_u8.pow(6); + + const DIVS: [(u8, vals::Clkdiv); 8] = [ + (1, vals::Clkdiv::DIV_BY_1), + (2, vals::Clkdiv::DIV_BY_2), + (3, vals::Clkdiv::DIV_BY_3), + (4, vals::Clkdiv::DIV_BY_4), + (5, vals::Clkdiv::DIV_BY_5), + (6, vals::Clkdiv::DIV_BY_6), + (7, vals::Clkdiv::DIV_BY_7), + (8, vals::Clkdiv::DIV_BY_8), + ]; + + // Quoting from SLAU 846 section 18.2.3.4: + // "Select oversampling by 3 or 8 to achieve higher speed with UARTclk/8 or UARTclk/3. In this case + // the receiver tolerance to clock deviation is reduced." + // + // "Select oversampling by 16 to increase the tolerance of the receiver to clock deviations. The + // maximum speed is limited to UARTclk/16." + // + // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock + // deviation. If no valid BRD value can be found satisifying the highest sample rate, then reduce + // sample rate until valid parameters are found. + const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)]; + + // 3x oversampling is not supported with manchester coding, DALI or IrDA. + let x3_invalid = { + let ctl0 = regs.ctl0().read(); + let irctl = regs.irctl().read(); + + ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren() + }; + let mut found = None; + + 'outer: for &(oversampling, hse_value) in &OVS { + if matches!(hse_value, vals::Hse::OVS3) && x3_invalid { + continue; + } + + // Verify that the selected oversampling does not require a clock faster than what the hardware + // is provided. + let Some(min_clock) = baudrate.checked_mul(oversampling as u32) else { + trace!( + "{}x oversampling would cause overflow for clock: {} Hz", + oversampling, + clock + ); + continue; + }; + + if min_clock > clock { + trace!("{} oversampling is too high for clock: {} Hz", oversampling, clock); + continue; + } + + for &(div, div_value) in &DIVS { + trace!( + "Trying div: {}, oversampling {} for {} baud", + div, + oversampling, + baudrate + ); + + let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else { + trace!("Calculating BRD overflowed: trying another divider"); + continue; + }; + + if ibrd < MIN_IBRD || fbrd > MAX_FBRD { + trace!("BRD was invalid: trying another divider"); + continue; + } + + found = Some((hse_value, div_value, ibrd, fbrd)); + break 'outer; + } + } + + let Some((hse, div, ibrd, fbrd)) = found else { + return Err(ConfigError::InvalidBaudRate); + }; + + regs.clkdiv().write(|w| { + w.set_ratio(div); + }); + + regs.ibrd().write(|w| { + w.set_divint(ibrd); + }); + + regs.fbrd().write(|w| { + w.set_divfrac(fbrd); + }); + + regs.ctl0().modify(|w| { + w.set_hse(hse); + }); + + Ok(()) +} + +/// Calculate the integer and fractional parts of the `BRD` value. +/// +/// Returns [`None`] if calculating this results in overflows. +/// +/// Values returned are `(ibrd, fbrd)` +fn calculate_brd(clock: u32, div: u8, baud: u32, oversampling: u8) -> Option<(u16, u8)> { + use fixed::types::U26F6; + + // Calculate BRD according to SLAU 846 section 18.2.3.4. + // + // BRD is a 22-bit value with 16 integer bits and 6 fractional bits. + // + // uart_clock = clock / div + // brd = ibrd.fbrd = uart_clock / (oversampling * baud)" + // + // It is tempting to rearrange the equation such that there is only a single division in + // order to reduce error. However this is wrong since the denominator ends up being too + // small to represent in 6 fraction bits. This means that FBRD would always be 0. + // + // Calculations are done in a U16F6 format. However the fixed crate has no such representation. + // U26F6 is used since it has the same number of fractional bits and we verify at the end that + // the integer part did not overflow. + let clock = U26F6::from_num(clock); + let div = U26F6::from_num(div); + let oversampling = U26F6::from_num(oversampling); + let baud = U26F6::from_num(baud); + + let uart_clock = clock.checked_div(div)?; + + // oversampling * baud + let denom = oversampling.checked_mul(baud)?; + // uart_clock / (oversampling * baud) + let brd = uart_clock.checked_div(denom)?; + + // Checked is used to determine overflow in the 10 most singificant bits since the + // actual representation of BRD is U16F6. + let ibrd = brd.checked_to_num::()?; + + // We need to scale FBRD's representation to an integer. + let fbrd_scale = U26F6::from_num(2_u32.checked_pow(U26F6::FRAC_NBITS)?); + + // It is suggested that 0.5 is added to ensure that any fractional parts round up to the next + // integer. If it doesn't round up then it'll get discarded which is okay. + let half = U26F6::from_num(1) / U26F6::from_num(2); + // fbrd = INT(((FRAC(BRD) * 64) + 0.5)) + let fbrd = brd + .frac() + .checked_mul(fbrd_scale)? + .checked_add(half)? + .checked_to_num::()?; + + Some((ibrd, fbrd)) +} + +fn read_with_error(r: Regs) -> Result { + let rx = r.rxdata().read(); + + if rx.frmerr() { + return Err(Error::Framing); + } else if rx.parerr() { + return Err(Error::Parity); + } else if rx.brkerr() { + return Err(Error::Break); + } else if rx.ovrerr() { + return Err(Error::Overrun); + } else if rx.nerr() { + return Err(Error::Noise); + } + + Ok(rx.data()) +} + +/// This function assumes CTL0.ENABLE is set (for errata cases). +fn busy(r: Regs) -> bool { + // Errata UART_ERR_08 + if cfg!(any( + mspm0g151x, mspm0g351x, mspm0l110x, mspm0l130x, mspm0l134x, mspm0c110x, + )) { + let stat = r.stat().read(); + // "Poll TXFIFO status and the CTL0.ENABLE register bit to identify BUSY status." + !stat.txfe() + } else { + r.stat().read().busy() + } +} + +// TODO: Implement when dma uart is implemented. +fn dma_enabled(_r: Regs) -> bool { + false +} + +pub(crate) trait SealedInstance { + fn info() -> &'static Info; + fn state() -> &'static State; + fn buffered_state() -> &'static BufferedState; +} + +macro_rules! impl_uart_instance { + ($instance: ident) => { + impl crate::uart::SealedInstance for crate::peripherals::$instance { + fn info() -> &'static crate::uart::Info { + use crate::interrupt::typelevel::Interrupt; + use crate::uart::Info; + + const INFO: Info = Info { + regs: crate::pac::$instance, + interrupt: crate::interrupt::typelevel::$instance::IRQ, + }; + &INFO + } + + fn state() -> &'static crate::uart::State { + use crate::uart::State; + + static STATE: State = State::new(); + &STATE + } + + fn buffered_state() -> &'static crate::uart::BufferedState { + use crate::uart::BufferedState; + + static STATE: BufferedState = BufferedState::new(); + &STATE + } + } + + impl crate::uart::Instance for crate::peripherals::$instance { + type Interrupt = crate::interrupt::typelevel::$instance; + } + }; +} + +macro_rules! impl_uart_tx_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::TxPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_uart_rx_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::RxPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_uart_cts_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::CtsPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_uart_rts_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::RtsPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +#[cfg(test)] +mod tests { + use super::calculate_brd; + + /// This is a smoke test based on the example in SLAU 846 section 18.2.3.4. + #[test] + fn datasheet() { + let brd = calculate_brd(40_000_000, 1, 19200, 16); + + assert!(matches!(brd, Some((130, 13)))); + } +} diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml index b6621c9c5..cc40b3109 100644 --- a/examples/mspm0g3507/Cargo.toml +++ b/examples/mspm0g3507/Cargo.toml @@ -17,5 +17,7 @@ defmt-rtt = "1.0.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } panic-semihosting = "0.6.0" +embedded-io-async = "0.6.1" + [profile.release] debug = 2 diff --git a/tests/mspm0/Cargo.toml b/tests/mspm0/Cargo.toml index 2d5b8cd52..5ba3e586b 100644 --- a/tests/mspm0/Cargo.toml +++ b/tests/mspm0/Cargo.toml @@ -13,6 +13,7 @@ teleprobe-meta = "1.1" embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "defmt" ] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt" ] } embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = [ "rt", "defmt", "unstable-pac", "time-driver-any" ] } embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"} @@ -24,6 +25,8 @@ cortex-m = { version = "0.7.6", features = [ "inline-asm", "critical-section-sin cortex-m-rt = "0.7.0" embedded-hal = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } +embedded-io = { version = "0.6.1", features = ["defmt-03"] } +embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } panic-probe = { version = "1.0.0", features = ["print-defmt"] } static_cell = "2" portable-atomic = { version = "1.5", features = ["critical-section"] } diff --git a/tests/mspm0/src/bin/uart_buffered.rs b/tests/mspm0/src/bin/uart_buffered.rs new file mode 100644 index 000000000..135ac1287 --- /dev/null +++ b/tests/mspm0/src/bin/uart_buffered.rs @@ -0,0 +1,115 @@ +#![no_std] +#![no_main] + +#[cfg(feature = "mspm0g3507")] +teleprobe_meta::target!(b"lp-mspm0g3507"); + +use defmt::{assert_eq, unwrap, *}; +use embassy_executor::Spawner; +use embassy_mspm0::uart::{BufferedInterruptHandler, BufferedUart, Config}; +use embassy_mspm0::{bind_interrupts, peripherals}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + UART1 => BufferedInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_mspm0::init(Default::default()); + info!("Hello World!"); + + // TODO: Allow creating a looped-back UART (so pins are not needed). + // Do not select default UART since the virtual COM port is attached to UART0. + #[cfg(any(feature = "mspm0g3507"))] + let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1); + + { + use embedded_io_async::{Read, Write}; + + let mut config = Config::default(); + config.loop_back_enable = true; + config.fifo_enable = false; + + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; + let mut uart = unwrap!(BufferedUart::new( + uart.reborrow(), + tx.reborrow(), + rx.reborrow(), + Irqs, + tx_buf, + rx_buf, + config + )); + + let mut buf = [0; 16]; + for (j, b) in buf.iter_mut().enumerate() { + *b = j as u8; + } + + unwrap!(uart.write_all(&buf).await); + unwrap!(uart.flush().await); + + unwrap!(uart.read_exact(&mut buf).await); + for (j, b) in buf.iter().enumerate() { + assert_eq!(*b, j as u8); + } + + // Buffer is unclogged, should be able to write again. + unwrap!(uart.write_all(&buf).await); + unwrap!(uart.flush().await); + + unwrap!(uart.read_exact(&mut buf).await); + for (j, b) in buf.iter().enumerate() { + assert_eq!(*b, j as u8); + } + } + + info!("Blocking buffered"); + { + use embedded_io::{Read, Write}; + + let mut config = Config::default(); + config.loop_back_enable = true; + config.fifo_enable = false; + + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; + let mut uart = unwrap!(BufferedUart::new( + uart.reborrow(), + tx.reborrow(), + rx.reborrow(), + Irqs, + tx_buf, + rx_buf, + config + )); + + let mut buf = [0; 16]; + + for (j, b) in buf.iter_mut().enumerate() { + *b = j as u8; + } + + unwrap!(uart.write_all(&buf)); + unwrap!(uart.blocking_flush()); + unwrap!(uart.read_exact(&mut buf)); + + for (j, b) in buf.iter().enumerate() { + assert_eq!(*b, j as u8); + } + + // Buffer is unclogged, should be able to write again. + unwrap!(uart.write_all(&buf)); + unwrap!(uart.blocking_flush()); + unwrap!(uart.read_exact(&mut buf)); + + for (j, b) in buf.iter().enumerate() { + assert_eq!(*b, j as u8, "at {}", j); + } + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} -- cgit From 510adb3b69daffec533292140221be36e2ed5ba2 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Tue, 22 Jul 2025 18:30:31 -0500 Subject: nxp: make docs generate --- .github/ci/doc.sh | 1 + embassy-nxp/Cargo.toml | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 06c61f8c0..90662af82 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -26,6 +26,7 @@ docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup +docserver-builder -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 01f57c4e2..078a3a7d8 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -3,6 +3,16 @@ name = "embassy-nxp" version = "0.1.0" edition = "2021" +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nxp-v$VERSION/embassy-nxp/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/src/" +features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers. + +flavors = [ + { regex_feature = "lpc55", target = "thumbv8m.main-none-eabihf" }, + { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" }, +] + [dependencies] cortex-m = "0.7.7" cortex-m-rt = "0.7.0" -- cgit From e64c23076d2c003efe60419eab6b86630d7886b4 Mon Sep 17 00:00:00 2001 From: Chris Storah Date: Wed, 23 Jul 2025 12:38:58 +1000 Subject: Updated version of stm32-data and added c071 and c051 into ci.sh --- ci.sh | 3 +++ embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/rcc/c0.rs | 11 ++--------- tests/stm32/Cargo.toml | 1 + tests/stm32/build.rs | 1 + tests/stm32/src/common.rs | 10 +++++++++- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/ci.sh b/ci.sh index 229ddaae8..b2277a6d7 100755 --- a/ci.sh +++ b/ci.sh @@ -102,6 +102,8 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c051f6,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ @@ -299,6 +301,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb --artifact-dir out/tests/stm32c071rb \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 248639385..8f3a471af 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-dded8a33a460ae0eb182aee3ccb048beb659982b" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-dded8a33a460ae0eb182aee3ccb048beb659982b", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index d44914719..b9773d1af 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -187,20 +187,13 @@ pub(crate) unsafe fn init(config: Config) { hse: hse, rtc: rtc, - #[cfg(any(stm32c071))] - hsi48: hsi, - // TODO lsi: None, lse: None, + hsi48: None, ); - #[cfg(not(any(stm32c071)))] - let r = RCC.ccipr(); - #[cfg(any(stm32c071))] - let r = RCC.ccipr1(); - - r.modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); + RCC.ccipr().modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); } mod max { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8d10f6593..7c32c0ce1 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,6 +7,7 @@ autobins = false [features] stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] +stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"] stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 722671bf1..556d77a20 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -12,6 +12,7 @@ fn main() -> Result<(), Box> { // too little RAM to run from RAM. feature = "stm32f103c8", // 20 kb feature = "stm32c031c6", // 6 kb + feature = "stm32c071rb", // 24 kb feature = "stm32l073rz", // 20 kb feature = "stm32h503rb", // 32 kb // no VTOR, so interrupts can't work when running from RAM diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 829f2cff0..a4d8048ce 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -34,6 +34,8 @@ teleprobe_meta::target!(b"nucleo-stm32u5a5zj"); teleprobe_meta::target!(b"nucleo-stm32h563zi"); #[cfg(feature = "stm32c031c6")] teleprobe_meta::target!(b"nucleo-stm32c031c6"); +#[cfg(feature = "stm32c071rb")] +teleprobe_meta::target!(b"nucleo-stm32c071rb"); #[cfg(feature = "stm32l073rz")] teleprobe_meta::target!(b"nucleo-stm32l073rz"); #[cfg(feature = "stm32l152re")] @@ -186,6 +188,12 @@ define_peris!( SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); +#[cfg(feature = "stm32c071rb")] +define_peris!( + UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); #[cfg(feature = "stm32l496zg")] define_peris!( UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, @@ -271,7 +279,7 @@ pub fn config() -> Config { #[allow(unused_mut)] let mut config = Config::default(); - #[cfg(feature = "stm32c031c6")] + #[cfg(any(feature = "stm32c031c6", feature = "stm32c071rb"))] { config.rcc.hsi = Some(Hsi { sys_div: HsiSysDiv::DIV1, // 48Mhz -- cgit From bb29fdd3e2b864325bbdea53810843cc2447d3c3 Mon Sep 17 00:00:00 2001 From: Chris Storah Date: Wed, 23 Jul 2025 12:50:01 +1000 Subject: Formatting update to resolve rustfmt error --- embassy-stm32/src/rcc/c0.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index b9773d1af..763f1b19c 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -186,14 +186,15 @@ pub(crate) unsafe fn init(config: Config) { hsiker: hsiker, hse: hse, rtc: rtc, - + // TODO lsi: None, lse: None, hsi48: None, ); - RCC.ccipr().modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); + RCC.ccipr() + .modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); } mod max { -- cgit From 487b42f62cd11292d421831a8943de01b2b25a39 Mon Sep 17 00:00:00 2001 From: Chris Storah Date: Wed, 23 Jul 2025 15:52:36 +1000 Subject: Added missing guard for hsi48. Updated use of removed enums from stm32-data u5 chip --- embassy-stm32/src/flash/u5.rs | 22 +++++++++++----------- embassy-stm32/src/rcc/c0.rs | 1 + 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 131caa195..2d06fdc67 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -30,19 +30,19 @@ pub(crate) unsafe fn enable_blocking_write() { #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().write(|w| { - w.set_pg(pac::flash::vals::SeccrPg::B_0X1); + w.set_pg(true); }); #[cfg(not(feature = "trustzone-secure"))] pac::FLASH.nscr().write(|w| { - w.set_pg(pac::flash::vals::NscrPg::B_0X1); + w.set_pg(true); }); } pub(crate) unsafe fn disable_blocking_write() { #[cfg(feature = "trustzone-secure")] - pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0)); + pac::FLASH.seccr().write(|w| w.set_pg(false)); #[cfg(not(feature = "trustzone-secure"))] - pac::FLASH.nscr().write(|w| w.set_pg(pac::flash::vals::NscrPg::B_0X0)); + pac::FLASH.nscr().write(|w| w.set_pg(false)); } pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { @@ -65,19 +65,19 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_pnb(sector.index_in_bank); // TODO: add check for bank swap w.set_bker(match sector.bank { - FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, - FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, + FlashBank::Bank1 => false, + FlashBank::Bank2 => true, _ => unreachable!(), }); }); #[cfg(not(feature = "trustzone-secure"))] pac::FLASH.nscr().modify(|w| { - w.set_per(pac::flash::vals::NscrPer::B_0X1); + w.set_per(true); w.set_pnb(sector.index_in_bank); // TODO: add check for bank swap w.set_bker(match sector.bank { - FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, - FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, + FlashBank::Bank1 => false, + FlashBank::Bank2 => true, _ => unreachable!(), }); }); @@ -95,11 +95,11 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(feature = "trustzone-secure")] pac::FLASH .seccr() - .modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0)); + .modify(|w| w.set_per(false)); #[cfg(not(feature = "trustzone-secure"))] pac::FLASH .nscr() - .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0)); + .modify(|w| w.set_per(false)); clear_all_err(); ret } diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 763f1b19c..c2295bab6 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -190,6 +190,7 @@ pub(crate) unsafe fn init(config: Config) { // TODO lsi: None, lse: None, + #[cfg(crs)] hsi48: None, ); -- cgit From 420cbb437fb37a0679859a459ed3b45239b60467 Mon Sep 17 00:00:00 2001 From: Chris Storah Date: Wed, 23 Jul 2025 15:55:14 +1000 Subject: Fix formatting of u5 file --- embassy-stm32/src/flash/u5.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 2d06fdc67..6c3d4b422 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -93,13 +93,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E let ret: Result<(), Error> = blocking_wait_ready(); #[cfg(feature = "trustzone-secure")] - pac::FLASH - .seccr() - .modify(|w| w.set_per(false)); + pac::FLASH.seccr().modify(|w| w.set_per(false)); #[cfg(not(feature = "trustzone-secure"))] - pac::FLASH - .nscr() - .modify(|w| w.set_per(false)); + pac::FLASH.nscr().modify(|w| w.set_per(false)); clear_all_err(); ret } -- cgit From cf9856255e1c2abf3ad2164ce669645f5da098c6 Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Wed, 23 Jul 2025 10:08:41 +0200 Subject: Make MSI calibration configurabke. Refine detection and handling of shared clock sources between MSIS and MSIK --- embassy-stm32/src/rcc/u5.rs | 101 +++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index e7fe50f33..0c7dc8ecc 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -64,6 +64,28 @@ pub struct Pll { pub divr: Option, } +#[derive(Clone, Copy, PartialEq)] +pub enum MsiAutoCalibration { + /// MSI auto-calibration is disabled + Disabled, + /// MSIS is given priority for auto-calibration + MSIS, + /// MSIK is given priority for auto-calibration + MSIK, +} + +impl MsiAutoCalibration { + const fn default() -> Self { + MsiAutoCalibration::Disabled + } +} + +impl Default for MsiAutoCalibration { + fn default() -> Self { + Self::default() + } +} + #[derive(Clone, Copy)] pub struct Config { // base clock sources @@ -95,6 +117,7 @@ pub struct Config { /// Per-peripheral kernel clock selection muxes pub mux: super::mux::ClockMux, + pub auto_calibration: MsiAutoCalibration, } impl Config { @@ -116,6 +139,7 @@ impl Config { voltage_range: VoltageScale::RANGE1, ls: crate::rcc::LsConfig::new(), mux: super::mux::ClockMux::default(), + auto_calibration: MsiAutoCalibration::default(), } } } @@ -133,7 +157,8 @@ pub(crate) unsafe fn init(config: Config) { let lse_calibration_freq = match config.ls.lse { Some(lse_config) => { - if lse_config.peripherals_clocked && (31_000..=34_000).contains(&lse_config.frequency.0) { + // Allow +/- 5% tolerance for LSE frequency + if lse_config.peripherals_clocked && (31_100..=34_400).contains(&lse_config.frequency.0) { Some(lse_config.frequency) } else { None @@ -167,11 +192,19 @@ pub(crate) unsafe fn init(config: Config) { w.set_msipllen(false); w.set_msison(true); }); + let msis = if let (Some(freq), MsiAutoCalibration::MSIS) = (lse_calibration_freq, config.auto_calibration) { + // Enable the MSIS auto-calibration feature + RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS)); + RCC.cr().modify(|w| w.set_msipllen(true)); + calculate_calibrated_msi_frequency(range, freq) + } else { + msirange_to_hertz(range) + }; while !RCC.cr().read().msisrdy() {} - msirange_to_hertz(range) + msis }); - let msik = config.msik.map(|range| { + let mut msik = config.msik.map(|range| { // Check MSI output per RM0456 § 11.4.10 match config.voltage_range { VoltageScale::RANGE4 => { @@ -195,24 +228,38 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().modify(|w| { w.set_msikon(true); }); - if lse_calibration_freq.is_some() { + let msik = if let (Some(freq), MsiAutoCalibration::MSIK) = (lse_calibration_freq, config.auto_calibration) { // Enable the MSIK auto-calibration feature RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK)); RCC.cr().modify(|w| w.set_msipllen(true)); - } - while !RCC.cr().read().msikrdy() {} - if let Some(freq) = lse_calibration_freq { - let msik_freq = calculate_calibrated_msi_frequency(range, freq); - if config.msis == config.msik { - // If MSIS and MSIK are the same range both will be auto calibrated to the same frequency - msis = Some(msik_freq) - } - msik_freq + calculate_calibrated_msi_frequency(range, freq) } else { msirange_to_hertz(range) - } + }; + while !RCC.cr().read().msikrdy() {} + msik }); + // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source. + if let Some(lse_freq) = lse_calibration_freq { + if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) { + if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) { + // Clock source is shared, both will be auto calibrated. + match config.auto_calibration { + MsiAutoCalibration::MSIS => { + // MSIS and MSIK are using the same clock source, recalibrate + msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq)); + } + MsiAutoCalibration::MSIK => { + // MSIS and MSIK are using the same clock source, recalibrate + msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq)); + } + _ => {} + } + } + } + } + let hsi = config.hsi.then(|| { RCC.cr().modify(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} @@ -559,27 +606,13 @@ impl MsiFraction { } } -/// Get the calibration fraction for a given MSI range -/// Based on STM32U5 datasheet table for LSE = 32.768 kHz fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { - match range { - Msirange::RANGE_48MHZ => MsiFraction::new(1465, 1), // Range 0: 48.005 MHz - Msirange::RANGE_24MHZ => MsiFraction::new(1465, 2), // Range 1: 24.003 MHz - Msirange::RANGE_16MHZ => MsiFraction::new(1465, 3), // Range 2: 16.002 MHz - Msirange::RANGE_12MHZ => MsiFraction::new(1465, 4), // Range 3: 12.001 MHz - Msirange::RANGE_4MHZ => MsiFraction::new(122, 1), // Range 4: 3.998 MHz - Msirange::RANGE_2MHZ => MsiFraction::new(61, 1), // Range 5: 1.999 MHz - Msirange::RANGE_1_33MHZ => MsiFraction::new(122, 3), // Range 6: 1.333 MHz - Msirange::RANGE_1MHZ => MsiFraction::new(61, 2), // Range 7: 0.999 MHz - Msirange::RANGE_3_072MHZ => MsiFraction::new(94, 1), // Range 8: 3.08 MHz - Msirange::RANGE_1_536MHZ => MsiFraction::new(47, 1), // Range 9: 1.54 MHz - Msirange::RANGE_1_024MHZ => MsiFraction::new(94, 3), // Range 10: 1.027 MHz - Msirange::RANGE_768KHZ => MsiFraction::new(47, 2), // Range 11: 0.77 MHz - Msirange::RANGE_400KHZ => MsiFraction::new(12, 1), // Range 12: 393 kHz - Msirange::RANGE_200KHZ => MsiFraction::new(6, 1), // Range 13: 196.6 kHz - Msirange::RANGE_133KHZ => MsiFraction::new(4, 1), // Range 14: 131 kHz - Msirange::RANGE_100KHZ => MsiFraction::new(3, 1), // Range 15: 98.3 kHz - } + // Exploiting the MSIx internals to make calculations compact + let denominator = (range as u32 & 0x03) + 1; + // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet + let numerator = [1465, 122, 94, 12][(range as u32 >> 2) as usize]; + + MsiFraction::new(numerator, denominator) } /// Calculate the calibrated MSI frequency for a given range and LSE frequency -- cgit From e9211682a1a7067ae3a1fac36f94d981aab44912 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Jul 2025 12:23:51 +0200 Subject: stm32: do not run stm32c071rb tests. --- ci.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci.sh b/ci.sh index 6be6e98e4..94f70aae8 100755 --- a/ci.sh +++ b/ci.sh @@ -385,6 +385,9 @@ rm out/tests/pimoroni-pico-plus-2/pwm rm out/tests/rpi-pico/pwm rm out/tests/rpi-pico/cyw43-perf +# tests are implemented but the HIL test farm doesn't actually have this board yet +rm -rf out/tests/stm32c071rb + if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit -- cgit From 4abacac2522b93a7f1d44c353b81f5f5054ed7cc Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 23 Jul 2025 15:20:25 +0100 Subject: stm32/wb: Add memory manager to GATT example --- examples/stm32wb/src/bin/gatt_server.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 041dc0cf5..9864fa026 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -27,6 +27,7 @@ use embassy_stm32_wpan::hci::vendor::event::{self, AttributeHandle, VendorEvent} use embassy_stm32_wpan::hci::{BdAddr, Event}; use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; use embassy_stm32_wpan::sub::ble::Ble; +use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -38,7 +39,7 @@ bind_interrupts!(struct Irqs{ const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { /* How to make this work: @@ -70,6 +71,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); @@ -221,6 +223,11 @@ async fn main(_spawner: Spawner) { } } +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + fn get_bd_addr() -> BdAddr { let mut bytes = [0u8; 6]; -- cgit From af4a75e4932cdca2e3fecfde06c6ced39659873c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 23 Jul 2025 15:33:27 +0100 Subject: stm32/build: Remove extra braces from generated code --- embassy-stm32/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f4781380c..73860c64a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1918,9 +1918,9 @@ fn main() { } g.extend(quote!( - pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio {{ - unsafe {{ crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) }} - }} + pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio { + unsafe { crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) } + } )); // ======== -- cgit From 978a007baf4e1ef345af7b7e622cecd1fc01e415 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Wed, 23 Jul 2025 16:56:34 +0200 Subject: add missing `Debug` and `defmt::Format` derives for `embassy_rp::gpio` --- embassy-rp/src/gpio.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 9b5faac15..f79bf8948 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -26,6 +26,7 @@ static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [const { AtomicWaker::new() /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Level { /// Logical low. Low, @@ -53,6 +54,7 @@ impl From for bool { /// Represents a pull setting for an input. #[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Pull { /// No pull. None, @@ -64,6 +66,7 @@ pub enum Pull { /// Drive strength of an output #[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Drive { /// 2 mA drive. _2mA, @@ -76,6 +79,7 @@ pub enum Drive { } /// Slew rate of an output #[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SlewRate { /// Fast slew rate. Fast, @@ -85,6 +89,7 @@ pub enum SlewRate { /// A GPIO bank with up to 32 pins. #[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Bank { /// Bank 0. Bank0 = 0, @@ -108,6 +113,8 @@ pub struct DormantWakeConfig { } /// GPIO input driver. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Input<'d> { pin: Flex<'d>, } @@ -358,6 +365,8 @@ impl<'d> Future for InputFuture<'d> { } /// GPIO output driver. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Output<'d> { pin: Flex<'d>, } @@ -445,6 +454,8 @@ impl<'d> Output<'d> { } /// GPIO output open-drain. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OutputOpenDrain<'d> { pin: Flex<'d>, } @@ -592,6 +603,8 @@ impl<'d> OutputOpenDrain<'d> { /// This pin can be either an input or output pin. The output level register bit will remain /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output /// mode. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Flex<'d> { pin: Peri<'d, AnyPin>, } @@ -864,6 +877,8 @@ impl<'d> Drop for Flex<'d> { } /// Dormant wake driver. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DormantWake<'w> { pin: Peri<'w, AnyPin>, cfg: DormantWakeConfig, -- cgit From f819b0d63c4b670f2aef3e8e9b35b0745090052d Mon Sep 17 00:00:00 2001 From: ROMemories <152802150+ROMemories@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:51:33 +0200 Subject: feat(embedded-hal)!: rely on v1.0 traits for `SpiBus` on `BlockingAsync` --- embassy-embedded-hal/src/adapter/blocking_async.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index bafc31583..bc965fbdd 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -63,16 +63,16 @@ where impl embedded_hal_async::spi::ErrorType for BlockingAsync where - E: embedded_hal_1::spi::Error, - T: blocking::spi::Transfer + blocking::spi::Write, + E: embedded_hal_async::spi::Error, + T: embedded_hal_1::spi::SpiBus, { type Error = E; } impl embedded_hal_async::spi::SpiBus for BlockingAsync where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, + E: embedded_hal_async::spi::Error, + T: embedded_hal_1::spi::SpiBus, { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) @@ -84,21 +84,17 @@ where } async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer(data)?; + self.wrapped.read(data)?; Ok(()) } async fn transfer(&mut self, read: &mut [u8], write: &[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)?; + self.wrapped.transfer(read, write)?; Ok(()) } async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer(data)?; + self.wrapped.transfer_in_place(data)?; Ok(()) } } -- cgit From 00824a900c6babf9727708357527b2a2807aa673 Mon Sep 17 00:00:00 2001 From: ROMemories <152802150+ROMemories@users.noreply.github.com> Date: Wed, 23 Jul 2025 17:08:54 +0200 Subject: feat(embedded-hal)!: rely on v1.0 traits for `I2c` on `BlockingAsync` --- embassy-embedded-hal/src/adapter/blocking_async.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index bc965fbdd..3b6e0ec00 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,5 +1,3 @@ -use embedded_hal_02::blocking; - /// Wrapper that implements async traits using blocking implementations. /// /// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations. @@ -24,7 +22,7 @@ impl BlockingAsync { impl embedded_hal_1::i2c::ErrorType for BlockingAsync where E: embedded_hal_1::i2c::Error + 'static, - T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, + T: embedded_hal_1::i2c::I2c, { type Error = E; } @@ -32,7 +30,7 @@ where impl embedded_hal_async::i2c::I2c for BlockingAsync where E: embedded_hal_1::i2c::Error + 'static, - T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, + T: embedded_hal_1::i2c::I2c, { async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.wrapped.read(address, read) @@ -51,9 +49,7 @@ where address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() + self.wrapped.transaction(address, operations) } } -- cgit From fd3cdfcf251225fb333870f0341ae9ce416f54ad Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Thu, 24 Jul 2025 13:26:10 +0200 Subject: Introduce configration options for Pll fast modes. Ensure that the auto calibration is applied to an active clock. --- embassy-stm32/src/rcc/u5.rs | 54 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 0c7dc8ecc..ae0ef73f4 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -5,7 +5,7 @@ pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; -use crate::pac::rcc::vals::{Hseext, Msipllsel, Msirgsel, Pllmboost, Pllrge}; +use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; #[cfg(all(peri_usb_otg_hs))] pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; use crate::pac::{FLASH, PWR, RCC}; @@ -72,12 +72,30 @@ pub enum MsiAutoCalibration { MSIS, /// MSIK is given priority for auto-calibration MSIK, + /// MSIS with fast mode (always on) + MsisFast, + /// MSIK with fast mode (always on) + MsikFast, } impl MsiAutoCalibration { const fn default() -> Self { MsiAutoCalibration::Disabled } + + fn base_mode(&self) -> Self { + match self { + MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled, + MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS, + MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK, + MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS, + MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK, + } + } + + fn is_fast(&self) -> bool { + matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast) + } } impl Default for MsiAutoCalibration { @@ -159,7 +177,23 @@ pub(crate) unsafe fn init(config: Config) { Some(lse_config) => { // Allow +/- 5% tolerance for LSE frequency if lse_config.peripherals_clocked && (31_100..=34_400).contains(&lse_config.frequency.0) { - Some(lse_config.frequency) + // Check that the calibration is applied to an active clock + match ( + config.auto_calibration.base_mode(), + config.msis.is_some(), + config.msik.is_some(), + ) { + (MsiAutoCalibration::MSIS, true, _) => { + // MSIS is active and using LSE for auto-calibration + Some(lse_config.frequency) + } + (MsiAutoCalibration::MSIK, _, true) => { + // MSIK is active and using LSE for auto-calibration + Some(lse_config.frequency) + } + // improper configuration, no LSE calibration + _ => None, + } } else { None } @@ -192,7 +226,9 @@ pub(crate) unsafe fn init(config: Config) { w.set_msipllen(false); w.set_msison(true); }); - let msis = if let (Some(freq), MsiAutoCalibration::MSIS) = (lse_calibration_freq, config.auto_calibration) { + let msis = if let (Some(freq), MsiAutoCalibration::MSIS) = + (lse_calibration_freq, config.auto_calibration.base_mode()) + { // Enable the MSIS auto-calibration feature RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS)); RCC.cr().modify(|w| w.set_msipllen(true)); @@ -228,7 +264,9 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().modify(|w| { w.set_msikon(true); }); - let msik = if let (Some(freq), MsiAutoCalibration::MSIK) = (lse_calibration_freq, config.auto_calibration) { + let msik = if let (Some(freq), MsiAutoCalibration::MSIK) = + (lse_calibration_freq, config.auto_calibration.base_mode()) + { // Enable the MSIK auto-calibration feature RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK)); RCC.cr().modify(|w| w.set_msipllen(true)); @@ -242,10 +280,16 @@ pub(crate) unsafe fn init(config: Config) { // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source. if let Some(lse_freq) = lse_calibration_freq { + // Check if Fast mode should be used + if config.auto_calibration.is_fast() { + RCC.cr().modify(|w| { + w.set_msipllfast(Msipllfast::FAST); + }); + } if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) { if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) { // Clock source is shared, both will be auto calibrated. - match config.auto_calibration { + match config.auto_calibration.base_mode() { MsiAutoCalibration::MSIS => { // MSIS and MSIK are using the same clock source, recalibrate msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq)); -- cgit From 3394f3ab9d2c0640bbd0804d5fd28cc68153786d Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Thu, 24 Jul 2025 13:51:35 +0200 Subject: Panic on improper auto-calibration configurations --- embassy-stm32/src/rcc/u5.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index ae0ef73f4..d12416a72 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -191,9 +191,8 @@ pub(crate) unsafe fn init(config: Config) { // MSIK is active and using LSE for auto-calibration Some(lse_config.frequency) } - // improper configuration, no LSE calibration - _ => None, - } + // improper configuration + _ => panic!("MSIx auto-calibration is enabled for a source that has not been configured.") } } else { None } -- cgit From aa243e4d3e51719d32314ba47ea68674ecc6c95e Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Thu, 24 Jul 2025 18:08:29 +0200 Subject: Improved error checks, and some cleanup --- embassy-stm32/src/rcc/u5.rs | 72 ++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index d12416a72..7a7ffc939 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -173,31 +173,39 @@ pub(crate) unsafe fn init(config: Config) { PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); while !PWR.vosr().read().vosrdy() {} - let lse_calibration_freq = match config.ls.lse { - Some(lse_config) => { - // Allow +/- 5% tolerance for LSE frequency - if lse_config.peripherals_clocked && (31_100..=34_400).contains(&lse_config.frequency.0) { - // Check that the calibration is applied to an active clock - match ( - config.auto_calibration.base_mode(), - config.msis.is_some(), - config.msik.is_some(), - ) { - (MsiAutoCalibration::MSIS, true, _) => { - // MSIS is active and using LSE for auto-calibration - Some(lse_config.frequency) - } - (MsiAutoCalibration::MSIK, _, true) => { - // MSIK is active and using LSE for auto-calibration - Some(lse_config.frequency) - } - // improper configuration - _ => panic!("MSIx auto-calibration is enabled for a source that has not been configured.") } - } else { - None + let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled { + // LSE must be configured and peripherals clocked for MSI auto-calibration + let lse_config = config + .ls + .lse + .clone() + .expect("LSE must be configured for MSI auto-calibration"); + assert!(lse_config.peripherals_clocked); + + // Expect less than +/- 5% deviation for LSE frequency + if (31_100..=34_400).contains(&lse_config.frequency.0) { + // Check that the calibration is applied to an active clock + match ( + config.auto_calibration.base_mode(), + config.msis.is_some(), + config.msik.is_some(), + ) { + (MsiAutoCalibration::MSIS, true, _) => { + // MSIS is active and using LSE for auto-calibration + Some(lse_config.frequency) + } + (MsiAutoCalibration::MSIK, _, true) => { + // MSIK is active and using LSE for auto-calibration + Some(lse_config.frequency) + } + // improper configuration + _ => panic!("MSIx auto-calibration is enabled for a source that has not been configured."), } + } else { + panic!("LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration"); } - _ => None, + } else { + None }; let mut msis = config.msis.map(|range| { @@ -277,30 +285,28 @@ pub(crate) unsafe fn init(config: Config) { msik }); - // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source. if let Some(lse_freq) = lse_calibration_freq { - // Check if Fast mode should be used - if config.auto_calibration.is_fast() { - RCC.cr().modify(|w| { - w.set_msipllfast(Msipllfast::FAST); - }); - } + // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source. if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) { if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) { - // Clock source is shared, both will be auto calibrated. + // Clock source is shared, both will be auto calibrated, recalculate other frequency match config.auto_calibration.base_mode() { MsiAutoCalibration::MSIS => { - // MSIS and MSIK are using the same clock source, recalibrate msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq)); } MsiAutoCalibration::MSIK => { - // MSIS and MSIK are using the same clock source, recalibrate msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq)); } _ => {} } } } + // Check if Fast mode should be used + if config.auto_calibration.is_fast() { + RCC.cr().modify(|w| { + w.set_msipllfast(Msipllfast::FAST); + }); + } } let hsi = config.hsi.then(|| { -- cgit From 0d1e34d0fcc1fb995f0da46eede063cfbd9c962e Mon Sep 17 00:00:00 2001 From: Frank Stevenson Date: Thu, 24 Jul 2025 21:17:30 +0200 Subject: Minor cleanup --- embassy-stm32/src/rcc/u5.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 7a7ffc939..06895a99a 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -659,7 +659,7 @@ fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { // Exploiting the MSIx internals to make calculations compact let denominator = (range as u32 & 0x03) + 1; // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet - let numerator = [1465, 122, 94, 12][(range as u32 >> 2) as usize]; + let numerator = [1465, 122, 94, 12][range as usize >> 2]; MsiFraction::new(numerator, denominator) } -- cgit From b49d809346bb420c7994c75fa0121f6d28870c05 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 24 Jul 2025 23:29:54 +0200 Subject: Add dedup to doc job. --- .github/ci/doc.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 90662af82..9162b37ae 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -1,5 +1,7 @@ #!/bin/bash ## on push branch=main +## priority -10 +## dedup dequeue set -euxo pipefail -- cgit From 915513753aea689f73d1300acc069ac985be3a0b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 24 Jul 2025 23:30:36 +0200 Subject: Add dedup to book job. --- .github/ci/book.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ci/book.sh b/.github/ci/book.sh index 285cdc8fa..2466f53f5 100755 --- a/.github/ci/book.sh +++ b/.github/ci/book.sh @@ -1,5 +1,7 @@ #!/bin/bash ## on push branch=main +## priority -9 +## dedup dequeue set -euxo pipefail -- cgit From 9863406346fdf5defcb8fe8de4bb5d122fa0b05f Mon Sep 17 00:00:00 2001 From: Knaifhogg Date: Wed, 18 Jun 2025 08:26:12 +0200 Subject: fix: stm32 i2c slave blocking r/w This fixes an issue where the slave interface would time out when the master goes from a short write to a read (e.g. when accessing memory registers) with a START signal between. The previous implementation would expect the full buffer length to be written before starting to listen to new commands. This also adds debug trace printing which helped during implemention and testing. Places error checking into a function inspired from a C implementation of HAL. --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/i2c/v2.rs | 278 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 221 insertions(+), 58 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 38254ee40..02e75733e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -129,6 +129,7 @@ defmt = [ "embassy-net-driver/defmt", "embassy-time?/defmt", "embassy-usb-synopsys-otg/defmt", + "stm32-metapac/defmt" ] exti = [] diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 35dc91c86..e24cce5c6 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -36,11 +36,46 @@ impl Address { } } +enum ReceiveResult { + DataAvailable, + StopReceived, + NewStart, +} + +fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) { + if isr.tcr() { + trace!("interrupt: tcr"); + } + if isr.tc() { + trace!("interrupt: tc"); + } + if isr.addr() { + trace!("interrupt: addr"); + } + if isr.stopf() { + trace!("interrupt: stopf"); + } + if isr.nackf() { + trace!("interrupt: nackf"); + } + if isr.berr() { + trace!("interrupt: berr"); + } + if isr.arlo() { + trace!("interrupt: arlo"); + } + if isr.ovr() { + trace!("interrupt: ovr"); + } +} + pub(crate) unsafe fn on_interrupt() { let regs = T::info().regs; let isr = regs.isr().read(); if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() { + debug_print_interrupts(isr); + T::state().waker.wake(); } @@ -193,49 +228,132 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { fn flush_txdr(&self) { if self.info.regs.isr().read().txis() { - self.info.regs.txdr().write(|w| w.set_txdata(0)); + trace!("Flush TXDATA with zeroes"); + self.info.regs.txdr().modify(|w| w.set_txdata(0)); } if !self.info.regs.isr().read().txe() { + trace!("Flush TXDR"); self.info.regs.isr().modify(|w| w.set_txe(true)) } } - fn wait_txe(&self, timeout: Timeout) -> Result<(), Error> { + fn error_occurred(&self, isr: &i2c::regs::Isr, timeout: Timeout) -> Result<(), Error> { + if isr.nackf() { + trace!("NACK triggered."); + self.info.regs.icr().modify(|reg| reg.set_nackcf(true)); + // NACK should be followed by STOP + if let Ok(()) = self.wait_stop(timeout) { + trace!("Got STOP after NACK, clearing flag."); + self.info.regs.icr().modify(|reg| reg.set_stopcf(true)); + } + self.flush_txdr(); + return Err(Error::Nack); + } else if isr.berr() { + trace!("BERR triggered."); + self.info.regs.icr().modify(|reg| reg.set_berrcf(true)); + self.flush_txdr(); + return Err(Error::Bus); + } else if isr.arlo() { + trace!("ARLO triggered."); + self.info.regs.icr().modify(|reg| reg.set_arlocf(true)); + self.flush_txdr(); + return Err(Error::Arbitration); + } else if isr.ovr() { + trace!("OVR triggered."); + self.info.regs.icr().modify(|reg| reg.set_ovrcf(true)); + return Err(Error::Overrun); + } + return Ok(()); + } + + fn wait_txis(&self, timeout: Timeout) -> Result<(), Error> { + let mut first_loop = true; + loop { let isr = self.info.regs.isr().read(); - if isr.txe() { + self.error_occurred(&isr, timeout)?; + if isr.txis() { + trace!("TXIS"); return Ok(()); - } else if isr.berr() { - self.info.regs.icr().write(|reg| reg.set_berrcf(true)); - return Err(Error::Bus); - } else if isr.arlo() { - self.info.regs.icr().write(|reg| reg.set_arlocf(true)); - return Err(Error::Arbitration); - } else if isr.nackf() { - self.info.regs.icr().write(|reg| reg.set_nackcf(true)); - self.flush_txdr(); - return Err(Error::Nack); } + { + if first_loop { + trace!("Waiting for TXIS..."); + first_loop = false; + } + } timeout.check()?; } } - fn wait_rxne(&self, timeout: Timeout) -> Result<(), Error> { + fn wait_stop_or_err(&self, timeout: Timeout) -> Result<(), Error> { + loop { + let isr = self.info.regs.isr().read(); + self.error_occurred(&isr, timeout)?; + if isr.stopf() { + trace!("STOP triggered."); + self.info.regs.icr().modify(|reg| reg.set_stopcf(true)); + return Ok(()); + } + timeout.check()?; + } + } + fn wait_stop(&self, timeout: Timeout) -> Result<(), Error> { loop { let isr = self.info.regs.isr().read(); - if isr.rxne() { + if isr.stopf() { + trace!("STOP triggered."); + self.info.regs.icr().modify(|reg| reg.set_stopcf(true)); return Ok(()); - } else if isr.berr() { - self.info.regs.icr().write(|reg| reg.set_berrcf(true)); - return Err(Error::Bus); - } else if isr.arlo() { - self.info.regs.icr().write(|reg| reg.set_arlocf(true)); - return Err(Error::Arbitration); - } else if isr.nackf() { - self.info.regs.icr().write(|reg| reg.set_nackcf(true)); - self.flush_txdr(); - return Err(Error::Nack); + } + timeout.check()?; + } + } + + fn wait_af(&self, timeout: Timeout) -> Result<(), Error> { + loop { + let isr = self.info.regs.isr().read(); + if isr.nackf() { + trace!("AF triggered."); + self.info.regs.icr().modify(|reg| reg.set_nackcf(true)); + return Ok(()); + } + timeout.check()?; + } + } + + fn wait_rxne(&self, timeout: Timeout) -> Result { + let mut first_loop = true; + + loop { + let isr = self.info.regs.isr().read(); + self.error_occurred(&isr, timeout)?; + if isr.stopf() { + trace!("STOP when waiting for RXNE."); + if self.info.regs.isr().read().rxne() { + trace!("Data received with STOP."); + return Ok(ReceiveResult::DataAvailable); + } + trace!("STOP triggered without data."); + return Ok(ReceiveResult::StopReceived); + } else if isr.rxne() { + trace!("RXNE."); + return Ok(ReceiveResult::DataAvailable); + } else if isr.addr() { + // Another addr event received, which means START was sent again + // which happens when accessing memory registers (common i2c interface design) + // e.g. master sends: START, write 1 byte (register index), START, read N bytes (until NACK) + // Possible to receive this flag at the same time as rxne, so check rxne first + trace!("START when waiting for RXNE. Ending receive loop."); + // Return without clearing ADDR so `listen` can catch it + return Ok(ReceiveResult::NewStart); + } + { + if first_loop { + trace!("Waiting for RXNE..."); + first_loop = false; + } } timeout.check()?; @@ -245,20 +363,10 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> { loop { let isr = self.info.regs.isr().read(); + self.error_occurred(&isr, timeout)?; if isr.tc() { return Ok(()); - } else if isr.berr() { - self.info.regs.icr().write(|reg| reg.set_berrcf(true)); - return Err(Error::Bus); - } else if isr.arlo() { - self.info.regs.icr().write(|reg| reg.set_arlocf(true)); - return Err(Error::Arbitration); - } else if isr.nackf() { - self.info.regs.icr().write(|reg| reg.set_nackcf(true)); - self.flush_txdr(); - return Err(Error::Nack); } - timeout.check()?; } } @@ -344,7 +452,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - if let Err(err) = self.wait_txe(timeout) { + if let Err(err) = self.wait_txis(timeout) { if send_stop { self.master_stop(); } @@ -459,7 +567,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - if let Err(err) = self.wait_txe(timeout) { + if let Err(err) = self.wait_txis(timeout) { self.master_stop(); return Err(err); } @@ -884,10 +992,11 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { // clear the address flag, will stop the clock stretching. // this should only be done after the dma transfer has been set up. info.regs.icr().modify(|reg| reg.set_addrcf(true)); + trace!("ADDRCF cleared (ADDR interrupt enabled, clock stretching ended)"); } // A blocking read operation - fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<(), Error> { + fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result { let completed_chunks = read.len() / 255; let total_chunks = if completed_chunks * 255 == read.len() { completed_chunks @@ -895,20 +1004,46 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { completed_chunks + 1 }; let last_chunk_idx = total_chunks.saturating_sub(1); + let total_len = read.len(); + let mut remaining_len = total_len; + for (number, chunk) in read.chunks_mut(255).enumerate() { - if number != 0 { + trace!( + "--- Slave RX transmission start - chunk: {}, expected (max) size: {}", + number, + chunk.len() + ); + if number == 0 { + Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); + } else { Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } + let mut index = 0; + for byte in chunk { // Wait until we have received something - self.wait_rxne(timeout)?; - - *byte = self.info.regs.rxdr().read().rxdata(); + match self.wait_rxne(timeout) { + Ok(ReceiveResult::StopReceived) | Ok(ReceiveResult::NewStart) => { + trace!("--- Slave RX transmission end (early)"); + return Ok(total_len - remaining_len); // Return N bytes read + } + Ok(ReceiveResult::DataAvailable) => { + *byte = self.info.regs.rxdr().read().rxdata(); + remaining_len = remaining_len.saturating_sub(1); + { + trace!("Slave RX data {}: {:#04x}", index, byte); + index = index + 1; + } + } + Err(e) => return Err(e), + }; } } + self.wait_stop_or_err(timeout)?; - Ok(()) + trace!("--- Slave RX transmission end"); + Ok(total_len - remaining_len) // Return N bytes read } // A blocking write operation @@ -922,19 +1057,36 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { let last_chunk_idx = total_chunks.saturating_sub(1); for (number, chunk) in write.chunks(255).enumerate() { - if number != 0 { + trace!( + "--- Slave TX transmission start - chunk: {}, size: {}", + number, + chunk.len() + ); + if number == 0 { + Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); + } else { Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } + let mut index = 0; + for byte in chunk { // Wait until we are allowed to send data - // (START has been ACKed or last byte when - // through) - self.wait_txe(timeout)?; + // (START has been ACKed or last byte when through) + self.wait_txis(timeout)?; + { + trace!("Slave TX data {}: {:#04x}", index, byte); + index = index + 1; + } self.info.regs.txdr().write(|w| w.set_txdata(*byte)); } } + self.wait_af(timeout)?; + self.flush_txdr(); + self.wait_stop_or_err(timeout)?; + + trace!("--- Slave TX transmission end"); Ok(()) } @@ -945,6 +1097,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { let state = self.state; self.info.regs.cr1().modify(|reg| { reg.set_addrie(true); + trace!("Enable ADDRIE"); }); poll_fn(|cx| { @@ -953,17 +1106,24 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { if !isr.addr() { Poll::Pending } else { + trace!("ADDR triggered (address match)"); // we do not clear the address flag here as it will be cleared by the dma read/write // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it match isr.dir() { - i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand { - kind: SlaveCommandKind::Write, - address: self.determine_matched_address()?, - })), - i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand { - kind: SlaveCommandKind::Read, - address: self.determine_matched_address()?, - })), + i2c::vals::Dir::WRITE => { + trace!("DIR: write"); + Poll::Ready(Ok(SlaveCommand { + kind: SlaveCommandKind::Write, + address: self.determine_matched_address()?, + })) + } + i2c::vals::Dir::READ => { + trace!("DIR: read"); + Poll::Ready(Ok(SlaveCommand { + kind: SlaveCommandKind::Read, + address: self.determine_matched_address()?, + })) + } } } }) @@ -971,7 +1131,9 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { } /// Respond to a write command. - pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> { + /// + /// Returns total number of bytes received. + pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result { let timeout = self.timeout(); self.slave_read_internal(read, timeout) } @@ -1025,7 +1187,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { w.set_rxdmaen(false); w.set_stopie(false); w.set_tcie(false); - }) + }); }); let total_received = poll_fn(|cx| { -- cgit From 3329089412e3ab367893eb975d611be49c8f5c5d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Jul 2025 00:11:42 +0200 Subject: embassy-embedded-hal: make time feature non-default default features considered harmful. --- ci.sh | 2 ++ embassy-embedded-hal/Cargo.toml | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 94f70aae8..f4db1da03 100755 --- a/ci.sh +++ b/ci.sh @@ -40,6 +40,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ + --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi --features time \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \ diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index aab6e0f1e..8277aa291 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -19,7 +19,6 @@ target = "x86_64-unknown-linux-gnu" [features] time = ["dep:embassy-time"] -default = ["time"] [dependencies] embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } -- cgit