From 1b9220993fb5cfd093a2ac953dd9da3578f9c78f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 10 Dec 2025 19:02:36 +0100 Subject: tiny improvements for nrf docs --- embassy-nrf/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 2f5ad352f..5d9f0e463 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -798,9 +798,9 @@ pub fn init(config: config::Config) -> Peripherals { // Chips with a certain chip type-specific build code or higher have an // improved APPROTECT ("hardware and software controlled access port protection") // which needs explicit action by the firmware to keep it unlocked - // See https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect + // See https://docs.nordicsemi.com/bundle/ps_nrf52840/page/dif.html#d402e184 - // UICR.APPROTECT = SwDisabled + // UICR.APPROTECT = HwDisabled let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); needs_reset |= res == WriteResult::Written; // APPROTECT.DISABLE = SwDisabled -- cgit From 55d0720b44cbdb2061866bf692de42e38e3d23b1 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 11 Dec 2025 12:04:09 +0100 Subject: feat: add support for buffered write mode for rram --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/rramc.rs | 173 ++++++++++++++++++++++++++-- examples/nrf54l15/src/bin/nvmc.rs | 44 ------- examples/nrf54l15/src/bin/rramc.rs | 44 +++++++ examples/nrf54l15/src/bin/rramc_buffered.rs | 59 ++++++++++ 5 files changed, 265 insertions(+), 56 deletions(-) delete mode 100644 examples/nrf54l15/src/bin/nvmc.rs create mode 100644 examples/nrf54l15/src/bin/rramc.rs create mode 100644 examples/nrf54l15/src/bin/rramc_buffered.rs diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 9c53e66a7..56bb15f42 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * added: support for nrf54l10 and nrf54l05 * added: expose uicr write functions * added: support for nrf54lm20a +- added: support buffered rram for nrf54 ## 0.8.0 - 2025-09-30 diff --git a/embassy-nrf/src/rramc.rs b/embassy-nrf/src/rramc.rs index 521ac4ee7..961e0a535 100644 --- a/embassy-nrf/src/rramc.rs +++ b/embassy-nrf/src/rramc.rs @@ -1,5 +1,6 @@ //! Resistive Random-Access Memory Controller driver. +use core::marker::PhantomData; use core::{ptr, slice}; use embedded_storage::nor_flash::{ @@ -9,11 +10,28 @@ use embedded_storage::nor_flash::{ use crate::peripherals::RRAMC; use crate::{Peri, pac}; +/// Unbuffered RRAMC mode. +pub struct Unbuffered; + +/// Buffered RRAMC mode. +pub struct Buffered; + +trait SealedRramMode {} + +/// Operating modes for RRAMC +#[allow(private_bounds)] +pub trait RramMode: SealedRramMode {} + +impl SealedRramMode for Unbuffered {} +impl RramMode for Unbuffered {} +impl SealedRramMode for Buffered {} +impl RramMode for Buffered {} + // // Export Nvmc alias and page size for downstream compatibility // /// RRAM-backed `Nvmc` compatibile driver. -pub type Nvmc<'d> = Rramc<'d>; +pub type Nvmc<'d> = Rramc<'d, Unbuffered>; /// Emulated page size. RRAM does not use pages. This exists only for downstream compatibility. pub const PAGE_SIZE: usize = 4096; @@ -44,16 +62,27 @@ impl NorFlashError for Error { /// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage` /// traits. -pub struct Rramc<'d> { +pub struct Rramc<'d, MODE: RramMode> { _p: Peri<'d, RRAMC>, + _d: PhantomData, +} + +impl<'d> Rramc<'d, Unbuffered> { + /// Create Rramc driver. + pub fn new(_p: Peri<'d, RRAMC>) -> Rramc<'d, Unbuffered> { + Self { _p, _d: PhantomData } + } } -impl<'d> Rramc<'d> { +impl<'d, const BUFFER_SIZE_BYTES: usize> Rramc<'d, Buffered> { /// Create Rramc driver. - pub fn new(_p: Peri<'d, RRAMC>) -> Self { - Self { _p } + pub fn new_buffered(_p: Peri<'d, RRAMC>) -> Rramc<'d, Buffered> { + assert!(BUFFER_SIZE_BYTES > 0 && BUFFER_SIZE_BYTES <= 512); + Self { _p, _d: PhantomData } } +} +impl<'d, MODE: RramMode> Rramc<'d, MODE> { fn regs() -> pac::rramc::Rramc { pac::RRAMC } @@ -63,18 +92,48 @@ impl<'d> Rramc<'d> { while !p.ready().read().ready() {} } + fn enable_read(&self) { + Self::regs().config().write(|w| w.set_wen(false)); + } + + fn finish_write(&mut self) { + self.enable_read(); + self.wait_ready(); + } +} + +impl<'d> Rramc<'d, Unbuffered> { + fn wait_ready_write(&mut self) { + let p = Self::regs(); + while !p.readynext().read().readynext() {} + } + + fn enable_write(&self) { + Self::regs().config().write(|w| { + w.set_wen(true); + w.set_writebufsize(pac::rramc::vals::Writebufsize::UNBUFFERED) + }); + } +} + +impl<'d, const SIZE: usize> Rramc<'d, Buffered> { fn wait_ready_write(&mut self) { let p = Self::regs(); while !p.readynext().read().readynext() {} while !p.bufstatus().writebufempty().read().empty() {} } - fn enable_read(&self) { - Self::regs().config().write(|w| w.set_wen(false)); + fn commit(&self) { + let p = Self::regs(); + p.tasks_commitwritebuf().write_value(1); + while !p.bufstatus().writebufempty().read().empty() {} } fn enable_write(&self) { - Self::regs().config().write(|w| w.set_wen(true)); + Self::regs().config().write(|w| { + w.set_wen(true); + w.set_writebufsize(pac::rramc::vals::Writebufsize::from_bits(SIZE as _)) + }); } } @@ -83,13 +142,13 @@ impl<'d> Rramc<'d> { // implement the traits for downstream compatibility. // -impl<'d> MultiwriteNorFlash for Rramc<'d> {} +impl<'d> MultiwriteNorFlash for Rramc<'d, Unbuffered> {} -impl<'d> ErrorType for Rramc<'d> { +impl<'d> ErrorType for Rramc<'d, Unbuffered> { type Error = Error; } -impl<'d> ReadNorFlash for Rramc<'d> { +impl<'d> ReadNorFlash for Rramc<'d, Unbuffered> { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -107,7 +166,7 @@ impl<'d> ReadNorFlash for Rramc<'d> { } } -impl<'d> NorFlash for Rramc<'d> { +impl<'d> NorFlash for Rramc<'d, Unbuffered> { const WRITE_SIZE: usize = WRITE_LINE_SIZE; const ERASE_SIZE: usize = PAGE_SIZE; @@ -139,11 +198,100 @@ impl<'d> NorFlash for Rramc<'d> { self.wait_ready_write(); } } + self.finish_write(); + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if offset as usize % Self::WRITE_SIZE != 0 || bytes.len() % Self::WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + self.enable_write(); + self.wait_ready(); + + unsafe { + let p_src = bytes.as_ptr() as *const u32; + let p_dst = offset as *mut u32; + let words = bytes.len() / 4; + for i in 0..words { + let w = ptr::read_unaligned(p_src.add(i)); + ptr::write_volatile(p_dst.add(i), w); + if (i + 1) % (Self::WRITE_SIZE / 4) == 0 { + self.wait_ready_write(); + } + } + } self.enable_read(); self.wait_ready(); Ok(()) } +} + +impl<'d, const BUFFER_SIZE_BYTES: usize> MultiwriteNorFlash for Rramc<'d, Buffered> {} + +impl<'d, const BUFFER_SIZE_BYTES: usize> ErrorType for Rramc<'d, Buffered> { + type Error = Error; +} + +impl<'d, const BUFFER_SIZE_BYTES: usize> ReadNorFlash for Rramc<'d, Buffered> { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + + let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl<'d, const BUFFER_SIZE_BYTES: usize> NorFlash for Rramc<'d, Buffered> { + const WRITE_SIZE: usize = WRITE_LINE_SIZE; + const ERASE_SIZE: usize = PAGE_SIZE; + + // RRAM can overwrite in-place, so emulate page erases by overwriting the page bytes with 0xFF. + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to < from || to as usize > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { + return Err(Error::Unaligned); + } + + self.enable_write(); + self.wait_ready(); + + // Treat each emulated page separately so callers can rely on post‑erase read‑back + // returning 0xFF just like on real NOR flash. + let buf = [0xFFu8; BUFFER_SIZE_BYTES]; + for page_addr in (from..to).step_by(Self::ERASE_SIZE) { + let page_end = page_addr + Self::ERASE_SIZE as u32; + for line_addr in (page_addr..page_end).step_by(BUFFER_SIZE_BYTES) { + unsafe { + let src = buf.as_ptr() as *const u32; + let dst = line_addr as *mut u32; + for i in 0..(Self::WRITE_SIZE / 4) { + core::ptr::write_volatile(dst.add(i), core::ptr::read_unaligned(src.add(i))); + } + } + self.wait_ready_write(); + } + } + self.commit(); + self.finish_write(); + Ok(()) + } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { if offset as usize + bytes.len() > FLASH_SIZE { @@ -169,6 +317,7 @@ impl<'d> NorFlash for Rramc<'d> { } } + self.commit(); self.enable_read(); self.wait_ready(); Ok(()) diff --git a/examples/nrf54l15/src/bin/nvmc.rs b/examples/nrf54l15/src/bin/nvmc.rs deleted file mode 100644 index f990604cd..000000000 --- a/examples/nrf54l15/src/bin/nvmc.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![no_std] -#![no_main] - -use defmt::{info, unwrap}; -use embassy_executor::Spawner; -use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - info!("Hello RRAMC NVMC!"); - - let mut f = Nvmc::new(p.RRAMC); - - const ADDR: u32 = 0x80000; - let mut buf = [0u8; 4]; - - info!("Reading..."); - unwrap!(f.read(ADDR, &mut buf)); - info!("Read: {=[u8]:x}", buf); - - info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + PAGE_SIZE as u32)); - - info!("Reading..."); - unwrap!(f.read(ADDR, &mut buf)); - info!("Read: {=[u8]:x}", buf); - - info!("Writing..."); - // 16 B (128-bit) write minimum - let out: [u8; 16] = [ - 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, - ]; - unwrap!(f.write(ADDR, &out)); - - info!("Reading..."); - // Can read arbitrary sizes - for addr in (ADDR..ADDR + 16).step_by(4) { - unwrap!(f.read(addr, &mut buf)); - info!("Read: {=[u8]:x}", buf); - } -} diff --git a/examples/nrf54l15/src/bin/rramc.rs b/examples/nrf54l15/src/bin/rramc.rs new file mode 100644 index 000000000..f990604cd --- /dev/null +++ b/examples/nrf54l15/src/bin/rramc.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Hello RRAMC NVMC!"); + + let mut f = Nvmc::new(p.RRAMC); + + const ADDR: u32 = 0x80000; + let mut buf = [0u8; 4]; + + info!("Reading..."); + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(ADDR, ADDR + PAGE_SIZE as u32)); + + info!("Reading..."); + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Writing..."); + // 16 B (128-bit) write minimum + let out: [u8; 16] = [ + 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, + ]; + unwrap!(f.write(ADDR, &out)); + + info!("Reading..."); + // Can read arbitrary sizes + for addr in (ADDR..ADDR + 16).step_by(4) { + unwrap!(f.read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); + } +} diff --git a/examples/nrf54l15/src/bin/rramc_buffered.rs b/examples/nrf54l15/src/bin/rramc_buffered.rs new file mode 100644 index 000000000..06c9585ed --- /dev/null +++ b/examples/nrf54l15/src/bin/rramc_buffered.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_nrf::rramc::{Buffered, PAGE_SIZE, Rramc}; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Hello RRAMC NVMC!"); + + // Buffer 8 word lines + let mut f: Rramc<'_, Buffered<128>> = Rramc::new_buffered(p.RRAMC); + + const ADDR: u32 = 0x80000; + let mut buf = [0u8; 4]; + + info!("Reading..."); + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(ADDR, ADDR + PAGE_SIZE as u32)); + + info!("Reading..."); + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Writing..."); + // 16 B (128-bit) write minimum + let out: [u8; 256] = [ + 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, + 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, + 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, + 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, + 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, + 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, + 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, + 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, + 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, + 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, + 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, + 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, + 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, + 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, + 0xdd, 0xdd, 0xdd, 0xdd, + ]; + unwrap!(f.write(ADDR, &out)); + + info!("Reading..."); + // Can read arbitrary sizes + for addr in (ADDR..ADDR + 256).step_by(4) { + unwrap!(f.read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); + } +} -- cgit From 67cf1f856bf50fae7ee519cfdbc8f5b6ff8d6706 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 11 Dec 2025 17:36:54 +0100 Subject: Update Rust, nightly, xtensa. --- .github/ci/build-xtensa.sh | 2 +- rust-toolchain-nightly.toml | 2 +- rust-toolchain.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index f07816861..cc56adb26 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh @@ -14,7 +14,7 @@ export PATH=$CARGO_HOME/bin:$PATH export CARGO_NET_GIT_FETCH_WITH_CLI=true cargo install espup --locked -espup install --toolchain-version 1.88.0.0 +espup install --toolchain-version 1.90.0.0 # Restore lockfiles if [ -f /ci/cache/lockfiles.tar ]; then diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index dde431bba..2a46473b7 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-09-26" +channel = "nightly-2025-12-11" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5d925c934..1f47bee1e 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.90" +channel = "1.92" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", -- cgit From 7083411ca255efdfe0ebf9cfba8e889c38630aee Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 12 Dec 2025 00:42:31 +0100 Subject: examples/std: fix warning. --- examples/std/src/bin/net_ppp.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs index 82272c798..685dbf3d3 100644 --- a/examples/std/src/bin/net_ppp.rs +++ b/examples/std/src/bin/net_ppp.rs @@ -52,7 +52,7 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri password: b"mypass", }; - runner + let r = runner .run(port, config, |ipv4| { let Some(addr) = ipv4.address else { warn!("PPP did not provide an IP address."); @@ -69,9 +69,10 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri }); stack.set_config_v4(config); }) - .await - .unwrap(); - unreachable!() + .await; + match r { + Err(e) => panic!("{:?}", e), + } } #[embassy_executor::task] -- cgit From 17f0f4baa489d87d9862f9ab91dc73530daf5b3e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 11 Dec 2025 15:42:46 -0800 Subject: [iMXRT] add a minimal DMA copy example While at that, also update the PACs to their latest versions. --- embassy-imxrt/Cargo.toml | 4 ++-- examples/mimxrt6/src/bin/dma.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 examples/mimxrt6/src/bin/dma.rs diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index c47756f10..81377579b 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -103,5 +103,5 @@ document-features = "0.2.7" paste = "1.0" # PACs -mimxrt685s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] } -mimxrt633s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] } +mimxrt685s-pac = { version = "0.5.0", optional = true, features = ["rt", "critical-section"] } +mimxrt633s-pac = { version = "0.5.0", optional = true, features = ["rt", "critical-section"] } diff --git a/examples/mimxrt6/src/bin/dma.rs b/examples/mimxrt6/src/bin/dma.rs new file mode 100644 index 000000000..b490efc6b --- /dev/null +++ b/examples/mimxrt6/src/bin/dma.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_imxrt::dma::copy; +use {defmt_rtt as _, panic_probe as _}; + +const BUFLEN: usize = 1024; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("Test memory-to-memory DMA transfers"); + + let src = [0x55u8; BUFLEN]; + let mut dst = [0u8; BUFLEN]; + + unsafe { copy(p.DMA0_CH0, &src, &mut dst) }.await; + assert!(dst == src); + + info!("DMA copy succeeded"); +} -- cgit From d7c7a5e76602e495d65da49f0f8eea80e3cc3fc2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 12 Dec 2025 00:48:16 +0100 Subject: executor: update ui test for nightly. --- .../tests/ui/bad_return_impl_future_nightly.stderr | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr index 3c3c9503b..e5e069ade 100644 --- a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr +++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr @@ -5,6 +5,10 @@ error[E0277]: task futures must resolve to `()` or `!` | ^^^^^^^^^^^^^^^^^^^^^^^^^ 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 HasOutput>::Output +help: the following other types implement trait `TaskReturnValue` + --> src/lib.rs + | + | impl TaskReturnValue for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` + | impl TaskReturnValue for Never {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ` ! as HasOutput>::Output` -- cgit From b28b61dc4aeb772502c61e4b0d9091569fac4a40 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 11 Dec 2025 15:56:27 -0800 Subject: [iMXRT] Add spi driver --- embassy-imxrt/src/flexcomm/mod.rs | 1 + embassy-imxrt/src/flexcomm/spi.rs | 1011 +++++++++++++++++++++++++++++++++ examples/mimxrt6/Cargo.toml | 2 + examples/mimxrt6/src/bin/spi-async.rs | 35 ++ examples/mimxrt6/src/bin/spi.rs | 51 ++ 5 files changed, 1100 insertions(+) create mode 100644 embassy-imxrt/src/flexcomm/spi.rs create mode 100644 examples/mimxrt6/src/bin/spi-async.rs create mode 100644 examples/mimxrt6/src/bin/spi.rs diff --git a/embassy-imxrt/src/flexcomm/mod.rs b/embassy-imxrt/src/flexcomm/mod.rs index 27794042b..ed87c7fb4 100644 --- a/embassy-imxrt/src/flexcomm/mod.rs +++ b/embassy-imxrt/src/flexcomm/mod.rs @@ -1,5 +1,6 @@ //! Implements Flexcomm interface wrapper for easier usage across modules +pub mod spi; pub mod uart; use paste::paste; diff --git a/embassy-imxrt/src/flexcomm/spi.rs b/embassy-imxrt/src/flexcomm/spi.rs new file mode 100644 index 000000000..9dd776ac7 --- /dev/null +++ b/embassy-imxrt/src/flexcomm/spi.rs @@ -0,0 +1,1011 @@ +//! Serial Peripheral Interface (SPI) driver. + +use core::future::{Future, poll_fn}; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +pub use embedded_hal_1::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; +use paste::paste; + +use crate::flexcomm::Clock; +use crate::gpio::{AnyPin, GpioPin as Pin}; +use crate::interrupt; +use crate::interrupt::typelevel::Interrupt; +use crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate}; +use crate::pac::spi0::cfg::{Cpha, Cpol}; + +/// Driver move trait. +#[allow(private_bounds)] +pub trait IoMode: sealed::Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl sealed::Sealed for Blocking {} +impl IoMode for Blocking {} + +/// Async mode. +pub struct Async; +impl sealed::Sealed for Async {} +impl IoMode for Async {} + +/// Spi errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now. +} + +/// Spi driver. +pub struct Spi<'a, M: IoMode> { + info: Info, + _phantom: PhantomData<&'a M>, +} + +impl<'a> Spi<'a, Blocking> { + /// Create a SPI driver in blocking mode. + pub fn new_blocking( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + miso.as_miso(); + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config) + } + + /// Create a TX-only SPI driver in blocking mode. + pub fn new_blocking_txonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config) + } + + /// Create an RX-only SPI driver in blocking mode. + pub fn new_blocking_rxonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + config: Config, + ) -> Self { + sck.as_sck(); + miso.as_miso(); + + Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config) + } + + /// Create an internal-loopback SPI driver in blocking mode. + /// + /// WARNING: This is only useful for testing as it doesn't use any + /// external pins. + pub fn new_blocking_loopback(_inner: Peri<'a, T>, config: Config) -> Self { + Self::new_inner(_inner, None, None, None, config) + } +} + +impl<'a, M: IoMode> Spi<'a, M> { + /// Read data from Spi blocking execution until done. + pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for word in data.iter_mut() { + // wait until we have data in the RxFIFO. + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) }); + + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + }); + + self.flush() + } + + /// Write data to Spi blocking execution until done. + pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for (i, word) in data.iter().enumerate() { + // wait until we have space in the TxFIFO. + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(*word as u16).len().bits(7) } + .rxignore() + .set_bit(); + + if i == data.len() - 1 { + w.eot().set_bit(); + } + + w + }); + } + }); + + self.flush() + } + + /// Transfer data to SPI blocking execution until done. + pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { + let len = read.len().max(write.len()); + + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for i in 0..len { + let wb = write.get(i).copied().unwrap_or(0); + + // wait until we have space in the TxFIFO. + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(wb as u16).len().bits(7) }; + + if i == len - 1 { + w.eot().set_bit(); + } + + w + }); + + // wait until we have data in the RxFIFO. + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + + let rb = self.info.regs.fiford().read().rxdata().bits() as u8; + + if let Some(r) = read.get_mut(i) { + *r = rb; + } + } + }); + + self.flush() + } + + /// Transfer data in place to SPI blocking execution until done. + pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for word in data { + // wait until we have space in the TxFIFO. + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16) }); + + // wait until we have data in the RxFIFO. + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + }); + + self.flush() + } + + /// Block execution until Spi is done. + pub fn flush(&mut self) -> Result<(), Error> { + let regs = self.info.regs; + while regs.stat().read().mstidle().bit_is_clear() {} + Ok(()) + } +} + +impl<'a> Spi<'a, Async> { + /// Create a SPI driver in async mode. + pub fn new_async( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + miso.as_miso(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config) + } + + /// Create a TX-only SPI driver in async mode. + pub fn new_async_txonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config) + } + + /// Create an RX-only SPI driver in async mode. + pub fn new_async_rxonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + sck.as_sck(); + miso.as_miso(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config) + } + + /// Create an internal-loopback SPI driver in async mode. + /// + /// WARNING: This is only useful for testing as it doesn't use any + /// external pins. + pub fn new_async_loopback( + _inner: Peri<'a, T>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, None, None, None, config) + } + + /// Read data from Spi async execution until done. + pub async fn async_read(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for word in data.iter_mut() { + // wait until we have data in the RxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().rxnotempty().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.rxlvl().set_bit().rxerr().set_bit()); + }, + ) + .await; + + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) }); + + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + + self.async_flush().await; + + Ok(()) + } + + /// Write data to Spi async execution until done. + pub async fn async_write(&mut self, data: &[u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for (i, word) in data.iter().enumerate() { + // wait until we have space in the TxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().txnotfull().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.txlvl().set_bit().txerr().set_bit()); + }, + ) + .await; + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(*word as u16).len().bits(7) } + .rxignore() + .set_bit(); + + if i == data.len() - 1 { + w.eot().set_bit(); + } + + w + }); + } + + self.async_flush().await; + + Ok(()) + } + + /// Transfer data to SPI async execution until done. + pub async fn async_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { + let len = read.len().max(write.len()); + + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for i in 0..len { + let wb = write.get(i).copied().unwrap_or(0); + + // wait until we have space in the TxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().txnotfull().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.fifotrig().write(|w| w.txlvlena().set_bit()); + me.info + .regs + .fifointenset() + .write(|w| w.txlvl().set_bit().txerr().set_bit()); + }, + ) + .await; + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(wb as u16).len().bits(7) }; + + if i == len - 1 { + w.eot().set_bit(); + } + + w + }); + + // wait until we have data in the RxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().rxnotempty().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.fifotrig().write(|w| w.rxlvlena().set_bit()); + me.info + .regs + .fifointenset() + .write(|w| w.rxlvl().set_bit().rxerr().set_bit()); + }, + ) + .await; + + let rb = self.info.regs.fiford().read().rxdata().bits() as u8; + + if let Some(r) = read.get_mut(i) { + *r = rb; + } + } + + self.async_flush().await; + + Ok(()) + } + + /// Transfer data in place to SPI async execution until done. + pub async fn async_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for word in data { + // wait until we have space in the TxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().txnotfull().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.txlvl().set_bit().txerr().set_bit()); + }, + ) + .await; + + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16) }); + + // wait until we have data in the RxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().rxnotempty().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.rxlvl().set_bit().rxerr().set_bit()); + }, + ) + .await; + + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + + self.async_flush().await; + + Ok(()) + } + + /// Async flush. + pub fn async_flush(&mut self) -> impl Future + use<'_, 'a> { + self.wait_for( + |me| { + if me.info.regs.stat().read().mstidle().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| w.mstidleen().set_bit()); + }, + ) + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + fn wait_for(&mut self, mut f: F, mut g: G) -> impl Future + use<'_, 'a, F, U, G> + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + poll_fn(move |cx| { + // Register waker before checking condition, to ensure that wakes/interrupts + // aren't lost between f() and g() + self.info.waker.register(cx.waker()); + let r = f(self); + + if r.is_pending() { + g(self); + } + + r + }) + } +} + +impl<'a, M: IoMode> Spi<'a, M> { + fn new_inner( + _inner: Peri<'a, T>, + sck: Option>, + mosi: Option>, + miso: Option>, + config: Config, + ) -> Self { + // REVISIT: allow selecting from multiple clocks. + let clk = Self::clock(&config); + + T::enable(clk); + T::into_spi(); + + Self::apply_config(T::info().regs, &config); + + let info = T::info(); + let regs = info.regs; + + critical_section::with(|_| match (sck.is_some(), mosi.is_some(), miso.is_some()) { + (true, true, true) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .set_bit() + .enablerx() + .set_bit() + .emptyrx() + .set_bit() + }); + } + (true, false, true) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .clear_bit() + .enablerx() + .set_bit() + .emptyrx() + .set_bit() + }); + } + (true, true, false) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .set_bit() + .enablerx() + .clear_bit() + .emptyrx() + .set_bit() + }); + } + (false, _, _) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .set_bit() + .enablerx() + .set_bit() + .emptyrx() + .set_bit() + }); + regs.cfg().modify(|_, w| w.loop_().enabled()); + } + _ => {} + }); + + Self { + info, + _phantom: PhantomData, + } + } + + fn set_config(&mut self, config: &Config) { + Self::apply_config(self.info.regs, config); + } + + fn clock(config: &Config) -> Clock { + const SFRO_CLOCK_SPEED_HZ: u32 = 16_000_000; + + if config.frequency > SFRO_CLOCK_SPEED_HZ { + Clock::Ffro + } else { + Clock::Sfro + } + } + + fn clock_frequency(clock: Clock) -> u32 { + match clock { + Clock::Sfro => 16_000_000, + Clock::Ffro => 48_000_000, + _ => unreachable!(), + } + } + + fn apply_config(regs: &'static crate::pac::spi0::RegisterBlock, config: &Config) { + let polarity = if config.mode.polarity == Polarity::IdleLow { + Cpol::Low + } else { + Cpol::High + }; + + let phase = if config.mode.phase == Phase::CaptureOnFirstTransition { + Cpha::Change + } else { + Cpha::Capture + }; + + let clk = Self::clock(config); + let div = Self::clock_frequency(clk) / config.frequency - 1; + + critical_section::with(|_| { + // disable SPI every time we need to modify configuration. + regs.cfg().modify(|_, w| w.enable().disabled()); + + regs.cfg().modify(|_, w| { + w.cpha() + .variant(phase) + .cpol() + .variant(polarity) + .loop_() + .disabled() + .master() + .master_mode() + }); + + regs.div().write(|w| unsafe { w.divval().bits(div as u16) }); + + regs.cfg().modify(|_, w| w.enable().enabled()); + }); + } +} + +/// Spi config. +#[derive(Clone)] +pub struct Config { + /// Frequency in Hertz. + pub frequency: u32, + /// SPI operating mode. + pub mode: Mode, +} + +impl Default for Config { + fn default() -> Self { + Self { + frequency: 1_000_000, + mode: MODE_0, + } + } +} + +struct Info { + regs: &'static crate::pac::spi0::RegisterBlock, + waker: &'static AtomicWaker, +} + +// SAFETY: safety for Send here is the same as the other accessors to +// unsafe blocks: it must be done from a single executor context. +// +// This is a temporary workaround -- a better solution might be to +// refactor Info to no longer maintain a reference to regs, but +// instead look up the correct register set and then perform +// operations within an unsafe block as we do for other peripherals +unsafe impl Send for Info {} + +trait SealedInstance { + fn info() -> Info; +} + +/// Spi interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let waker = T::info().waker; + let stat = T::info().regs.fifointstat().read(); + + if stat.perint().bit_is_set() { + T::info().regs.intenclr().write(|w| w.mstidle().clear_bit_by_one()); + } + + if stat.txlvl().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.txlvl().set_bit()); + } + + if stat.txerr().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.txerr().set_bit()); + } + + if stat.rxlvl().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.rxlvl().set_bit()); + } + + if stat.rxerr().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.rxerr().set_bit()); + } + + waker.wake(); + } +} + +/// Spi instance trait. +#[allow(private_bounds)] +pub trait Instance: crate::flexcomm::IntoSpi + SealedInstance + PeripheralType + 'static + Send { + /// Interrupt for this Spi instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_instance { + ($($n:expr),*) => { + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + #[inline] + fn info() -> Info { + static WAKER: AtomicWaker = AtomicWaker::new(); + + Info { + regs: unsafe { &*crate::pac::[]::ptr() }, + waker: &WAKER, + } + } + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + } + } + )* + } +} + +impl_instance!(0, 1, 2, 3, 4, 5, 6, 7, 14); + +mod sealed { + /// Seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for T {} + +/// IO configuration trait for Spi clk +pub trait SckPin: Pin + sealed::Sealed + PeripheralType { + /// convert the pin to appropriate function for Spi clk usage. + fn as_sck(&self); +} + +/// IO configuration trait for Spi mosi +pub trait MosiPin: Pin + sealed::Sealed + PeripheralType { + /// convert the pin to appropriate function for Spi mosi usage. + fn as_mosi(&self); +} + +/// IO configuration trait for Spi miso +pub trait MisoPin: Pin + sealed::Sealed + PeripheralType { + /// convert the pin to appropriate function for Spi miso usage. + fn as_miso(&self); +} + +macro_rules! impl_pin_trait { + ($fcn:ident, $mode:ident, $($pin:ident, $fn:ident),*) => { + paste! { + $( + impl [<$mode:camel Pin>] for crate::peripherals::$pin { + fn [](&self) { + // UM11147 table 530 pg 518 + self.set_function(crate::iopctl::Function::$fn) + .set_pull(Pull::None) + .enable_input_buffer() + .set_slew_rate(SlewRate::Standard) + .set_drive_strength(DriveStrength::Normal) + .disable_analog_multiplex() + .set_drive_mode(DriveMode::PushPull) + .set_input_inverter(Inverter::Disabled); + } + } + )* + } + } +} + +// FLEXCOMM0 +impl_pin_trait!(FLEXCOMM0, sck, PIO0_0, F1, PIO3_0, F5); +impl_pin_trait!(FLEXCOMM0, miso, PIO0_1, F1, PIO3_1, F5); +impl_pin_trait!(FLEXCOMM0, mosi, PIO0_2, F1, PIO3_2, F5); + +// FLEXCOMM1 +impl_pin_trait!(FLEXCOMM1, sck, PIO0_7, F1, PIO7_25, F1); +impl_pin_trait!(FLEXCOMM1, miso, PIO0_8, F1, PIO7_26, F1); +impl_pin_trait!(FLEXCOMM1, mosi, PIO0_9, F1, PIO7_28, F1); + +// FLEXCOMM2 +impl_pin_trait!(FLEXCOMM2, sck, PIO0_14, F1, PIO7_29, F5); +impl_pin_trait!(FLEXCOMM2, miso, PIO0_15, F1, PIO7_30, F5); +impl_pin_trait!(FLEXCOMM2, mosi, PIO0_16, F1, PIO7_31, F5); + +// FLEXCOMM3 +impl_pin_trait!(FLEXCOMM3, sck, PIO0_21, F1); +impl_pin_trait!(FLEXCOMM3, miso, PIO0_22, F1); +impl_pin_trait!(FLEXCOMM3, mosi, PIO0_23, F1); + +// FLEXCOMM4 +impl_pin_trait!(FLEXCOMM4, sck, PIO0_28, F1); +impl_pin_trait!(FLEXCOMM4, miso, PIO0_29, F1); +impl_pin_trait!(FLEXCOMM4, mosi, PIO0_30, F1); + +// FLEXCOMM5 +impl_pin_trait!(FLEXCOMM5, sck, PIO1_3, F1, PIO3_15, F5); +impl_pin_trait!(FLEXCOMM5, miso, PIO1_4, F1, PIO3_16, F5); +impl_pin_trait!(FLEXCOMM5, mosi, PIO1_5, F1, PIO3_17, F5); + +// FLEXCOMM6 +impl_pin_trait!(FLEXCOMM6, sck, PIO3_25, F1); +impl_pin_trait!(FLEXCOMM6, miso, PIO3_26, F1); +impl_pin_trait!(FLEXCOMM6, mosi, PIO3_27, F1); + +// FLEXCOMM7 +impl_pin_trait!(FLEXCOMM7, sck, PIO4_0, F1); +impl_pin_trait!(FLEXCOMM7, miso, PIO4_1, F1); +impl_pin_trait!(FLEXCOMM7, mosi, PIO4_2, F1); + +// FLEXCOMM14 +impl_pin_trait!(FLEXCOMM14, sck, PIO1_11, F1); +impl_pin_trait!(FLEXCOMM14, miso, PIO1_12, F1); +impl_pin_trait!(FLEXCOMM14, mosi, PIO1_13, F1); + +/// Spi Tx DMA trait. +#[allow(private_bounds)] +pub trait TxDma: crate::dma::Channel {} + +/// Spi Rx DMA trait. +#[allow(private_bounds)] +pub trait RxDma: crate::dma::Channel {} + +macro_rules! impl_dma { + ($fcn:ident, $mode:ident, $dma:ident) => { + paste! { + impl [<$mode Dma>] for crate::peripherals::$dma {} + } + }; +} + +impl_dma!(FLEXCOMM0, Rx, DMA0_CH0); +impl_dma!(FLEXCOMM0, Tx, DMA0_CH1); + +impl_dma!(FLEXCOMM1, Rx, DMA0_CH2); +impl_dma!(FLEXCOMM1, Tx, DMA0_CH3); + +impl_dma!(FLEXCOMM2, Rx, DMA0_CH4); +impl_dma!(FLEXCOMM2, Tx, DMA0_CH5); + +impl_dma!(FLEXCOMM3, Rx, DMA0_CH6); +impl_dma!(FLEXCOMM3, Tx, DMA0_CH7); + +impl_dma!(FLEXCOMM4, Rx, DMA0_CH8); +impl_dma!(FLEXCOMM4, Tx, DMA0_CH9); + +impl_dma!(FLEXCOMM5, Rx, DMA0_CH10); +impl_dma!(FLEXCOMM5, Tx, DMA0_CH11); + +impl_dma!(FLEXCOMM6, Rx, DMA0_CH12); +impl_dma!(FLEXCOMM6, Tx, DMA0_CH13); + +impl_dma!(FLEXCOMM7, Rx, DMA0_CH14); +impl_dma!(FLEXCOMM7, Tx, DMA0_CH15); + +impl_dma!(FLEXCOMM14, Rx, DMA0_CH16); +impl_dma!(FLEXCOMM14, Tx, DMA0_CH17); + +// ============================== + +impl<'d, M: IoMode> embedded_hal_02::blocking::spi::Transfer for Spi<'d, M> { + type Error = Error; + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.blocking_transfer_in_place(words)?; + Ok(words) + } +} + +impl<'d, M: IoMode> embedded_hal_02::blocking::spi::Write for Spi<'d, M> { + type Error = Error; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(words) + } +} + +impl embedded_hal_1::spi::Error for Error { + fn kind(&self) -> embedded_hal_1::spi::ErrorKind { + match *self {} + } +} + +impl<'d, M: IoMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { + type Error = Error; +} + +impl<'d, M: IoMode> embedded_hal_1::spi::SpiBus for Spi<'d, M> { + fn flush(&mut self) -> Result<(), Self::Error> { + self.flush() + } + + fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(words) + } + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(words) + } + + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + self.blocking_transfer(read, write) + } + + fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_transfer_in_place(words) + } +} + +impl<'d> embedded_hal_async::spi::SpiBus for Spi<'d, Async> { + async fn flush(&mut self) -> Result<(), Self::Error> { + self.async_flush().await; + + Ok(()) + } + + async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.async_write(words).await + } + + async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.async_read(words).await + } + + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + self.async_transfer(read, write).await + } + + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.async_transfer_in_place(words).await + } +} + +impl<'d, M: IoMode> SetConfig for Spi<'d, M> { + type Config = Config; + type ConfigError = (); + fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { + self.set_config(config); + + Ok(()) + } +} diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml index dc09e97e7..ada112833 100644 --- a/examples/mimxrt6/Cargo.toml +++ b/examples/mimxrt6/Cargo.toml @@ -21,6 +21,8 @@ embedded-hal-async = "1.0.0" mimxrt600-fcb = "0.2.2" panic-probe = { version = "1.0.0", features = ["print-defmt"] } +embedded-hal-bus = "0.3.0" +is31fl3743b-driver = { version = "0.1.1", features = ["is_blocking"] } # cargo build/run [profile.dev] diff --git a/examples/mimxrt6/src/bin/spi-async.rs b/examples/mimxrt6/src/bin/spi-async.rs new file mode 100644 index 000000000..aa56f7c42 --- /dev/null +++ b/examples/mimxrt6/src/bin/spi-async.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_imxrt::bind_interrupts; +use embassy_imxrt::flexcomm::spi::{InterruptHandler, Spi}; +use embassy_imxrt::peripherals::FLEXCOMM5; +use {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FLEXCOMM5 => InterruptHandler; +}); + +const BUFLEN: usize = 1024; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("Initializing SPI"); + + let mut spi = Spi::new_async(p.FLEXCOMM5, p.PIO1_3, p.PIO1_5, p.PIO1_4, Irqs, Default::default()); + + let mut rxbuf = [0x55; BUFLEN]; + let txbuf = [0xaa; BUFLEN]; + + for _ in 0..10 { + spi.async_transfer(&mut rxbuf, &txbuf).await.unwrap(); + assert!(rxbuf.iter().all(|b| *b == 0xaa)); + rxbuf.fill(0x55); + } + + info!("SPI transfers succeeded"); +} diff --git a/examples/mimxrt6/src/bin/spi.rs b/examples/mimxrt6/src/bin/spi.rs new file mode 100644 index 000000000..4854432e8 --- /dev/null +++ b/examples/mimxrt6/src/bin/spi.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_imxrt::flexcomm::spi::Spi; +use embassy_imxrt::gpio; +use embassy_time::{Delay, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use is31fl3743b_driver::{CSy, Is31fl3743b, SWx}; +use {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("Initializing SPI"); + + let cs = gpio::Output::new( + p.PIO1_6, + gpio::Level::Low, + gpio::DriveMode::PushPull, + gpio::DriveStrength::Normal, + gpio::SlewRate::Standard, + ); + + let spi = Spi::new_blocking(p.FLEXCOMM5, p.PIO1_3, p.PIO1_5, p.PIO1_4, Default::default()); + let delay = Delay; + + // One SPI device only on the SPI bus + let spi_dev = ExclusiveDevice::new(spi, cs, delay).unwrap(); + + // Instantiate IS31FL3743B device + let mut driver = Is31fl3743b::new(spi_dev).unwrap(); + + // Enable phase delay to help reduce power noise + let _ = driver.enable_phase_delay(); + // Set global current, check method documentation for more info + let _ = driver.set_global_current(90); + + let _ = driver.set_led_peak_current_bulk(SWx::SW1, CSy::CS1, &[100; 11 * 18]); + + // Driver is fully set up, we can now start turning on LEDs! + // Create a white breathing effect + loop { + for brightness in (0..=255_u8).chain((0..=255).rev()) { + let _ = driver.set_led_brightness_bulk(SWx::SW1, CSy::CS1, &[brightness; 11 * 18]); + Timer::after_micros(1).await; + } + } +} -- cgit From b1fe9c6955ff857e3729a0bb4727247e050fb7ae Mon Sep 17 00:00:00 2001 From: Gerhard de Clercq <11624490+Gerharddc@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:04:51 +0000 Subject: Add `run_until` function to std Executor as to support grafeul shutdown. --- embassy-executor/CHANGELOG.md | 1 + embassy-executor/src/arch/std.rs | 15 +++++++++++- examples/std/src/bin/tick_cancel.rs | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 examples/std/src/bin/tick_cancel.rs diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 5fbb8cf13..8f1db7de7 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added optional "earliest deadline first" EDF scheduling - Migrate `cortex-ar` to `aarch32-cpu`. The feature name `arch-cortex-ar` remains the same and legacy ARM architectures are not supported. +- Added `run_until` to `arch-std` variant of `Executor`. ## 0.9.1 - 2025-08-31 diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index c62ab723b..d4057144e 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -55,11 +55,24 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + self.run_until(init, || false); + unreachable!() + } + + /// Run the executor until a flag is raised. + /// + /// This function is identical to `Executor::run()` apart from offering a `done` flag to stop execution. + pub fn run_until(&'static mut self, init: impl FnOnce(Spawner), mut done: impl FnMut() -> bool) { init(self.inner.spawner()); loop { unsafe { self.inner.poll() }; - self.signaler.wait() + + if done() { + break; + } + + self.signaler.wait(); } } } diff --git a/examples/std/src/bin/tick_cancel.rs b/examples/std/src/bin/tick_cancel.rs new file mode 100644 index 000000000..54e44790f --- /dev/null +++ b/examples/std/src/bin/tick_cancel.rs @@ -0,0 +1,47 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::time::Duration; + +use embassy_executor::Executor; +use embassy_time::Timer; +use log::*; +use static_cell::StaticCell; + +#[embassy_executor::task] +async fn run() { + loop { + info!("tick"); + Timer::after_secs(1).await; + } +} + +static DONE: StaticCell = StaticCell::new(); +static EXECUTOR: StaticCell = StaticCell::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .format_timestamp_nanos() + .init(); + + let done = DONE.init(AtomicBool::new(false)); + let done_cb = || done.load(Ordering::Relaxed); + + let server_thread = thread::spawn(move || { + let executor = EXECUTOR.init(Executor::new()); + executor.run_until( + |spawner| { + spawner.spawn(run().unwrap()); + }, + done_cb, + ); + info!("Executor finished"); + }); + + thread::sleep(Duration::from_secs(5)); + + info!("Cancelling executor"); + done.store(true, Ordering::Relaxed); + + server_thread.join().unwrap(); +} -- cgit From d9c90df7578dda10d888941b8df5a2295373ca1f Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 12 Dec 2025 11:32:47 -0600 Subject: stm32/sdio: update transfer interface --- embassy-stm32/src/sdmmc/mod.rs | 64 ++++++++++++++++++++++++++++++++--------- embassy-stm32/src/sdmmc/sd.rs | 58 ++++++++++++++++++++----------------- embassy-stm32/src/sdmmc/sdio.rs | 27 ++++++++--------- 3 files changed, 97 insertions(+), 52 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 12086cd3a..e716fc348 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -7,6 +7,7 @@ use core::marker::PhantomData; use core::slice; use core::task::Poll; +use aligned::{A4, Aligned}; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::Cmd; @@ -139,16 +140,38 @@ impl Default for Signalling { } } +const fn aligned_mut(x: &mut [u32]) -> &mut Aligned { + let len = x.len() * 4; + unsafe { core::mem::transmute(slice::from_raw_parts_mut(x.as_mut_ptr() as _, len)) } +} + const fn slice8_mut(x: &mut [u32]) -> &mut [u8] { let len = x.len() * 4; unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } } +#[allow(unused)] +const fn slice32_mut(x: &mut Aligned) -> &mut [u32] { + let len = (size_of_val(x) + 4 - 1) / 4; + unsafe { slice::from_raw_parts_mut(x as *mut Aligned as *mut _, len) } +} + +const fn aligned_ref(x: &[u32]) -> &Aligned { + let len = x.len() * 4; + unsafe { core::mem::transmute(slice::from_raw_parts(x.as_ptr() as _, len)) } +} + const fn slice8_ref(x: &[u32]) -> &[u8] { let len = x.len() * 4; unsafe { slice::from_raw_parts(x.as_ptr() as _, len) } } +#[allow(unused)] +const fn slice32_ref(x: &Aligned) -> &[u32] { + let len = (size_of_val(x) + 4 - 1) / 4; + unsafe { slice::from_raw_parts(x as *const Aligned as *const _, len) } +} + /// Errors #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -187,6 +210,11 @@ enum PowerCtrl { On = 0b11, } +enum DatapathMode { + Block(BlockSize), + Byte, +} + fn get_waitresp_val(rlen: ResponseLen) -> u8 { match rlen { common_cmd::ResponseLen::Zero => 0, @@ -768,12 +796,16 @@ impl<'d> Sdmmc<'d> { #[allow(unused_variables)] fn prepare_datapath_read<'a>( &'a self, - buffer: &'a mut [u32], - block_size: BlockSize, - byte_mode: bool, + buffer: &'a mut Aligned, + mode: DatapathMode, ) -> WrappedTransfer<'a> { let regs = self.info.regs; + let (byte_mode, block_size) = match mode { + DatapathMode::Block(block_size) => (false, block_size as u8), + DatapathMode::Byte => (true, 0), + }; + // Command AND Data state machines must be idle self.wait_idle(); self.clear_interrupt_flags(); @@ -783,8 +815,11 @@ impl<'d> Sdmmc<'d> { // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] let transfer = unsafe { - self.dma - .read_unchecked(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) + self.dma.read_unchecked( + regs.fifor().as_ptr() as *mut u32, + slice32_mut(buffer), + DMA_TRANSFER_OPTIONS, + ) }; #[cfg(sdmmc_v2)] let transfer = { @@ -817,14 +852,14 @@ impl<'d> Sdmmc<'d> { /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - fn prepare_datapath_write<'a>( - &'a self, - buffer: &'a [u32], - block_size: BlockSize, - byte_mode: bool, - ) -> WrappedTransfer<'a> { + fn prepare_datapath_write<'a>(&'a self, buffer: &'a Aligned, mode: DatapathMode) -> WrappedTransfer<'a> { let regs = self.info.regs; + let (byte_mode, block_size) = match mode { + DatapathMode::Block(block_size) => (false, block_size as u8), + DatapathMode::Byte => (true, 0), + }; + // Command AND Data state machines must be idle self.wait_idle(); self.clear_interrupt_flags(); @@ -834,8 +869,11 @@ impl<'d> Sdmmc<'d> { // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] let transfer = unsafe { - self.dma - .write_unchecked(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) + self.dma.write_unchecked( + slice32_ref(buffer), + regs.fifor().as_ptr() as *mut u32, + DMA_TRANSFER_OPTIONS, + ) }; #[cfg(sdmmc_v2)] let transfer = { diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs index 6190226b8..20318bbfa 100644 --- a/embassy-stm32/src/sdmmc/sd.rs +++ b/embassy-stm32/src/sdmmc/sd.rs @@ -5,7 +5,10 @@ use sdio_host::emmc::{EMMC, ExtCSD}; use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; -use crate::sdmmc::{BlockSize, Error, Sdmmc, Signalling, block_size, bus_width_vals, slice8_mut, slice8_ref}; +use crate::sdmmc::{ + BlockSize, DatapathMode, Error, Sdmmc, Signalling, aligned_mut, aligned_ref, block_size, bus_width_vals, + slice8_mut, slice8_ref, +}; use crate::time::{Hertz, mhz}; /// Aligned data block for SDMMC transfers. @@ -230,10 +233,8 @@ impl<'a, 'b> StorageDevice<'a, 'b, Card> { }; let buffer = &mut cmd_block.0[..64 / 4]; - - let transfer = self - .sdmmc - .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); + let mode = DatapathMode::Block(block_size(size_of_val(buffer))); + let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode); self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 @@ -272,7 +273,9 @@ impl<'a, 'b> StorageDevice<'a, 'b, Card> { // Arm `OnDrop` after the buffer, so it will be dropped first - let transfer = self.sdmmc.prepare_datapath_read(scr, BlockSize::Size8, false); + let transfer = self + .sdmmc + .prepare_datapath_read(aligned_mut(scr), DatapathMode::Block(BlockSize::Size8)); self.sdmmc.cmd(sd_cmd::send_scr(), true)?; self.sdmmc.complete_datapath_transfer(transfer, true).await?; @@ -290,10 +293,9 @@ impl<'a, 'b> StorageDevice<'a, 'b, Card> { self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP let buffer = &mut cmd_block.as_mut()[..64 / 4]; + let mode = DatapathMode::Block(block_size(size_of_val(buffer))); - let transfer = self - .sdmmc - .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); + let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode); self.sdmmc.cmd(sd_cmd::sd_status(), true)?; self.sdmmc.complete_datapath_transfer(transfer, true).await?; @@ -398,9 +400,10 @@ impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { .cmd(common_cmd::set_block_length(size_of::() as u32), false) .unwrap(); // CMD16 - let transfer = self - .sdmmc - .prepare_datapath_read(&mut data_block.0, block_size(size_of::()), false); + let transfer = self.sdmmc.prepare_datapath_read( + aligned_mut(&mut data_block.0), + DatapathMode::Block(block_size(size_of::())), + ); self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; self.sdmmc.complete_datapath_transfer(transfer, true).await?; @@ -418,7 +421,7 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { /// Read a data block. #[inline] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + pub async fn read_block(&mut self, block_idx: u32, data_block: &mut DataBlock) -> Result<(), Error> { let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); let card_capacity = self.info.get_capacity(); @@ -431,9 +434,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { self.sdmmc .cmd(common_cmd::set_block_length(size_of::() as u32), false)?; // CMD16 - let transfer = self - .sdmmc - .prepare_datapath_read(&mut buffer.0, block_size(size_of::()), false); + let transfer = self.sdmmc.prepare_datapath_read( + aligned_mut(&mut data_block.0), + DatapathMode::Block(block_size(size_of::())), + ); self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; self.sdmmc.complete_datapath_transfer(transfer, true).await?; @@ -464,9 +468,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { self.sdmmc .cmd(common_cmd::set_block_length(size_of::() as u32), false)?; // CMD16 - let transfer = self - .sdmmc - .prepare_datapath_read(buffer, block_size(size_of::()), false); + let transfer = self.sdmmc.prepare_datapath_read( + aligned_mut(buffer), + DatapathMode::Block(block_size(size_of::())), + ); self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; self.sdmmc.complete_datapath_transfer(transfer, false).await?; @@ -497,9 +502,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { #[cfg(sdmmc_v1)] self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; - let transfer = self - .sdmmc - .prepare_datapath_write(&buffer.0, block_size(size_of::()), false); + let transfer = self.sdmmc.prepare_datapath_write( + aligned_ref(&buffer.0), + DatapathMode::Block(block_size(size_of::())), + ); #[cfg(sdmmc_v2)] self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; @@ -548,10 +554,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 // Setup write command - let transfer = self - .sdmmc - .prepare_datapath_write(buffer, block_size(size_of::()), false); - + let transfer = self.sdmmc.prepare_datapath_write( + aligned_ref(buffer), + DatapathMode::Block(block_size(size_of::())), + ); #[cfg(sdmmc_v2)] self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs index 1412b21fc..e436d68cb 100644 --- a/embassy-stm32/src/sdmmc/sdio.rs +++ b/embassy-stm32/src/sdmmc/sdio.rs @@ -1,10 +1,11 @@ use core::ops::{Deref, DerefMut}; +use aligned::{A4, Aligned}; use sdio_host::common_cmd::{R1, Rz, cmd}; use sdio_host::sd::BusWidth; use sdio_host::sd_cmd; -use crate::sdmmc::{Error, Sdmmc, block_size, slice8_mut, slice8_ref}; +use crate::sdmmc::{DatapathMode, Error, Sdmmc, aligned_mut, aligned_ref, block_size, slice8_mut, slice8_ref}; use crate::time::Hertz; /// Aligned data block for SDMMC transfers. @@ -39,7 +40,7 @@ impl DerefMut for DataBlock { /// Storage Device pub struct SerialDataInterface<'a, 'b> { /// Inner member - pub sdmmc: &'a mut Sdmmc<'b>, + sdmmc: &'a mut Sdmmc<'b>, } /// Card Storage Device @@ -101,9 +102,10 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> { ) }; - let transfer = self - .sdmmc - .prepare_datapath_read(buffer, block_size(size_of::()), false); + let transfer = self.sdmmc.prepare_datapath_read( + aligned_mut(buffer), + DatapathMode::Block(block_size(size_of::())), + ); self.sdmmc.cmd(cmd::(53, arg), true)?; self.sdmmc.complete_datapath_transfer(transfer, false).await?; @@ -113,12 +115,10 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> { } /// Read in multibyte mode using cmd53 - pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut [u32]) -> Result<(), Error> { + pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned) -> Result<(), Error> { let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - let transfer = self - .sdmmc - .prepare_datapath_read(buffer, block_size(size_of::()), true); + let transfer = self.sdmmc.prepare_datapath_read(buffer, DatapathMode::Byte); self.sdmmc.cmd(cmd::(53, arg), true)?; self.sdmmc.complete_datapath_transfer(transfer, false).await?; @@ -142,9 +142,10 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> { #[cfg(sdmmc_v1)] self.sdmmc.cmd(cmd::(53, arg), true)?; - let transfer = self - .sdmmc - .prepare_datapath_read(buffer, block_size(size_of::()), false); + let transfer = self.sdmmc.prepare_datapath_write( + aligned_ref(buffer), + DatapathMode::Block(block_size(size_of::())), + ); #[cfg(sdmmc_v2)] self.sdmmc.cmd(cmd::(53, arg), true)?; @@ -164,7 +165,7 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> { let transfer = self .sdmmc - .prepare_datapath_write(buffer, block_size(size_of::()), true); + .prepare_datapath_write(aligned_ref(buffer), DatapathMode::Byte); #[cfg(sdmmc_v2)] self.sdmmc.cmd(cmd::(53, arg), true)?; -- cgit From a1e0c8f09beea8d49ebeca2232152aec4c089ebf Mon Sep 17 00:00:00 2001 From: Kezi Date: Fri, 12 Dec 2025 23:11:33 +0100 Subject: fix copy paste bug in i2c pullup ? --- embassy-nrf/src/twim.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index abf9a923f..6c38ab3fe 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -174,7 +174,7 @@ impl<'d> Twim<'d> { }); w.set_drive1(gpiovals::Drive::D); } - if config.sda_pullup { + if config.scl_pullup { w.set_pull(gpiovals::Pull::PULLUP); } }); -- cgit