From 8e7d0e6406d8e701860a3138292ae87d75ddc8b0 Mon Sep 17 00:00:00 2001 From: TrAyZeN Date: Mon, 1 Dec 2025 10:57:41 +0100 Subject: stm32/i2c: Restrict listen to async I2c mode --- embassy-stm32/src/i2c/v2.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 6b213484c..4f105adef 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1673,6 +1673,22 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { Ok(()) } + /// Respond to a write command. + /// + /// 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) + } + + /// 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> { /// Listen for incoming I2C messages. /// /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. @@ -1713,22 +1729,6 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { .await } - /// Respond to a write command. - /// - /// 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) - } - - /// 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 write command. /// /// Returns the total number of bytes received. -- cgit From ac89dbd3447993f7940b60400fb3e2d479956836 Mon Sep 17 00:00:00 2001 From: TrAyZeN Date: Mon, 1 Dec 2025 11:25:06 +0100 Subject: stm32/i2c: Add blocking_listen --- embassy-stm32/src/i2c/v2.rs | 65 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4f105adef..933cca9cb 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1673,6 +1673,54 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { Ok(()) } + /// Listen for incoming I2C messages. + /// + /// This method blocks until the slave address is matched by a master. + pub fn blocking_listen(&mut self) -> Result { + let timeout = self.timeout(); + + self.info.regs.cr1().modify(|reg| { + reg.set_addrie(true); + trace!("Enable ADDRIE"); + }); + + loop { + let isr = self.info.regs.isr().read(); + if isr.addr() { + break; + } + timeout.check()?; + } + + 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 + self.slave_command() + } + + /// Determine the received slave command. + fn slave_command(&self) -> Result { + let isr = self.info.regs.isr().read(); + + match isr.dir() { + i2c::vals::Dir::WRITE => { + trace!("DIR: write"); + Ok(SlaveCommand { + kind: SlaveCommandKind::Write, + address: self.determine_matched_address()?, + }) + } + i2c::vals::Dir::READ => { + trace!("DIR: read"); + Ok(SlaveCommand { + kind: SlaveCommandKind::Read, + address: self.determine_matched_address()?, + }) + } + } + } + /// Respond to a write command. /// /// Returns total number of bytes received. @@ -1708,22 +1756,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { 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 => { - 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()?, - })) - } - } + Poll::Ready(self.slave_command()) } }) .await -- cgit From 52a9b08f0ca13d23bfb039c884a9101997c10567 Mon Sep 17 00:00:00 2001 From: TrAyZeN Date: Mon, 1 Dec 2025 11:51:33 +0100 Subject: chore: Update changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 2a99d0a96..6c36bc108 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -86,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/lcd: added implementation - change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) - fix: stm32/i2c v2: add stop flag on stop received +- stm32: Add blocking_listen for blocking I2C driver ## 0.4.0 - 2025-08-26 -- cgit From 57e572ca12182007d0dc421e4406a4a0a4695d99 Mon Sep 17 00:00:00 2001 From: Simon Börjesson Date: Mon, 8 Dec 2025 23:41:19 +0100 Subject: Improve cyw43 join handling --- cyw43/src/consts.rs | 4 ++- cyw43/src/control.rs | 73 +++++++++++++++++++++++++++++++++------------------- cyw43/src/lib.rs | 2 +- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs index c3f0dbfd8..e561d4794 100644 --- a/cyw43/src/consts.rs +++ b/cyw43/src/consts.rs @@ -177,9 +177,11 @@ pub(crate) enum Security { } #[allow(non_camel_case_types)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, num_enum::FromPrimitive)] #[repr(u8)] pub enum EStatus { + #[num_enum(default)] + Unknown = 0xFF, /// operation was successful SUCCESS = 0, /// operation failed diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 219198d1f..98f456ce4 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -12,11 +12,15 @@ use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; use crate::{PowerManagementMode, countries, events}; -/// Control errors. +/// Join errors. #[derive(Debug)] -pub struct Error { - /// Status code. - pub status: u32, +pub enum JoinError { + /// Network not found. + NetworkNotFound, + /// Failure to join network. Contains the status code from the SET_SSID event. + JoinFailure(u8), + /// Authentication failure for a secure network. + AuthenticationFailure, } /// Multicast errors. @@ -296,7 +300,7 @@ impl<'a> Control<'a> { } /// Join a network with the provided SSID using the specified options. - pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), Error> { + pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), JoinError> { self.set_iovar_u32("ampdu_ba_wsize", 8).await; if options.auth == JoinAuth::Open { @@ -367,40 +371,55 @@ impl<'a> Control<'a> { }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.wait_for_join(i).await + let secure_network = options.auth != JoinAuth::Open; + self.wait_for_join(i, secure_network).await } - async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { - self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); + async fn wait_for_join(&mut self, i: SsidInfo, secure_network: bool) -> Result<(), JoinError> { + self.events.mask.enable(&[Event::SET_SSID, Event::AUTH, Event::PSK_SUP]); let mut subscriber = self.events.queue.subscriber().unwrap(); // the actual join operation starts here // we make sure to enable events before so we don't miss any self.ioctl(IoctlType::Set, Ioctl::SetSsid, 0, &mut i.to_bytes()).await; - // to complete the join, we wait for a SET_SSID event - // we also save the AUTH status for the user, it may be interesting - let mut auth_status = 0; - let status = loop { + // To complete the join on an open network, we wait for a SET_SSID event with status SUCCESS + // For secured networks, we wait for a PSK_SUP event with status 6 "UNSOLICITED" + let result = loop { let msg = subscriber.next_message_pure().await; - if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { - auth_status = msg.header.status; - } else if msg.header.event_type == Event::SET_SSID { - // join operation ends with SET_SSID event - break msg.header.status; - } + + let status = EStatus::from(msg.header.status as u8); + match (msg.header.event_type, status, secure_network) { + // Join operation ends with SET_SSID event for open networks + (Event::SET_SSID, EStatus::SUCCESS, false) => break Ok(()), + (Event::SET_SSID, EStatus::NO_NETWORKS, _) => break Err(JoinError::NetworkNotFound), + (Event::SET_SSID, status, _) if status != EStatus::SUCCESS => { + break Err(JoinError::JoinFailure(status as u8)); + } + // Ignore PSK_SUP "ABORT" which is sometimes sent before successful join + (Event::PSK_SUP, EStatus::ABORT, true) => {} + // Event PSK_SUP with status 6 "UNSOLICITED" indicates success for secure networks + (Event::PSK_SUP, EStatus::UNSOLICITED, true) => break Ok(()), + // Events indicating authentication failure, possibly due to incorrect password + (Event::PSK_SUP, _, true) | (Event::AUTH, EStatus::FAIL, true) => { + break Err(JoinError::AuthenticationFailure); + } + _ => {} + }; }; self.events.mask.disable_all(); - if status == EStatus::SUCCESS { - // successful join - self.state_ch.set_link_state(LinkState::Up); - debug!("JOINED"); - Ok(()) - } else { - warn!("JOIN failed with status={} auth={}", status, auth_status); - Err(Error { status }) - } + match result { + Ok(()) => { + self.state_ch.set_link_state(LinkState::Up); + debug!("JOINED"); + } + Err(JoinError::JoinFailure(status)) => debug!("JOIN failed: status={}", status), + Err(JoinError::NetworkNotFound) => debug!("JOIN failed: network not found"), + Err(JoinError::AuthenticationFailure) => debug!("JOIN failed: authentication failure"), + }; + + result } /// Set GPIO pin on WiFi chip. diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index 82c636346..d723037c7 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -31,7 +31,7 @@ use ioctl::IoctlState; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; pub use crate::control::{ - AddMulticastAddressError, Control, Error as ControlError, JoinAuth, JoinOptions, ScanOptions, ScanType, Scanner, + AddMulticastAddressError, Control, JoinError, JoinAuth, JoinOptions, ScanOptions, ScanType, Scanner, }; pub use crate::runner::Runner; pub use crate::structs::BssInfo; -- cgit From 7087ad7d45396e139fcbd710084db0d9dac7ecb8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 9 Dec 2025 09:34:50 -0600 Subject: type-erase sdmmc --- embassy-stm32/src/sdmmc/mod.rs | 394 ++++++++++++++++++++++------------------- 1 file changed, 212 insertions(+), 182 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 37ef7099f..74a6f13fa 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -9,6 +9,7 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::mutex::Mutex; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::common_cmd::{self, Resp, ResponseLen}; use sdio_host::emmc::{EMMC, ExtCSD}; @@ -22,7 +23,7 @@ use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; -use crate::rcc::{self, RccPeripheral}; +use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -31,28 +32,11 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl InterruptHandler { - fn enable_interrupts() { - let regs = T::regs(); - regs.maskr().write(|w| { - w.set_dcrcfailie(true); - w.set_dtimeoutie(true); - w.set_dataendie(true); - w.set_dbckendie(true); - - #[cfg(sdmmc_v1)] - w.set_stbiterre(true); - #[cfg(sdmmc_v2)] - w.set_dabortie(true); - }); - } -} - impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - T::state().wake(); - let status = T::regs().star().read(); - T::regs().maskr().modify(|w| { + T::state().waker.wake(); + let status = T::info().regs.star().read(); + T::info().regs.maskr().modify(|w| { if status.dcrcfail() { w.set_dcrcfailie(false) } @@ -379,8 +363,10 @@ impl SdmmcPeripheral { } /// Sdmmc device -pub struct Sdmmc<'d, T: Instance> { - _peri: Peri<'d, T>, +pub struct Sdmmc<'d> { + info: &'static Info, + state: &'static State, + ker_clk: Hertz, #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, @@ -416,9 +402,9 @@ const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh const DATA_AF: AfType = CMD_AF; #[cfg(sdmmc_v1)] -impl<'d, T: Instance> Sdmmc<'d, T> { +impl<'d> Sdmmc<'d> { /// Create a new SDMMC driver, with 1 data lane. - pub fn new_1bit( + pub fn new_1bit( sdmmc: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, dma: Peri<'d, impl SdmmcDma>, @@ -451,7 +437,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } /// Create a new SDMMC driver, with 4 data lanes. - pub fn new_4bit( + pub fn new_4bit( sdmmc: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, dma: Peri<'d, impl SdmmcDma>, @@ -491,9 +477,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } #[cfg(sdmmc_v1)] -impl<'d, T: Instance> Sdmmc<'d, T> { +impl<'d> Sdmmc<'d> { /// Create a new SDMMC driver, with 8 data lanes. - pub fn new_8bit( + pub fn new_8bit( sdmmc: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, dma: Peri<'d, impl SdmmcDma>, @@ -541,9 +527,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } #[cfg(sdmmc_v2)] -impl<'d, T: Instance> Sdmmc<'d, T> { +impl<'d> Sdmmc<'d> { /// Create a new SDMMC driver, with 1 data lane. - pub fn new_1bit( + pub fn new_1bit( sdmmc: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, clk: Peri<'d, impl CkPin>, @@ -574,7 +560,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } /// Create a new SDMMC driver, with 4 data lanes. - pub fn new_4bit( + pub fn new_4bit( sdmmc: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, clk: Peri<'d, impl CkPin>, @@ -612,9 +598,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } #[cfg(sdmmc_v2)] -impl<'d, T: Instance> Sdmmc<'d, T> { +impl<'d> Sdmmc<'d> { /// Create a new SDMMC driver, with 8 data lanes. - pub fn new_8bit( + pub fn new_8bit( sdmmc: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, clk: Peri<'d, impl CkPin>, @@ -659,9 +645,24 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } } -impl<'d, T: Instance> Sdmmc<'d, T> { - fn new_inner( - sdmmc: Peri<'d, T>, +impl<'d> Sdmmc<'d> { + fn enable_interrupts(&self) { + let regs = self.info.regs; + regs.maskr().write(|w| { + w.set_dcrcfailie(true); + w.set_dtimeoutie(true); + w.set_dataendie(true); + w.set_dbckendie(true); + + #[cfg(sdmmc_v1)] + w.set_stbiterre(true); + #[cfg(sdmmc_v2)] + w.set_dabortie(true); + }); + } + + fn new_inner( + _sdmmc: Peri<'d, T>, #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, clk: Peri<'d, AnyPin>, cmd: Peri<'d, AnyPin>, @@ -680,8 +681,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - let regs = T::regs(); - regs.clkcr().write(|w| { + let info = T::info(); + let state = T::state(); + let ker_clk = T::frequency(); + + info.regs.clkcr().write(|w| { w.set_pwrsav(false); w.set_negedge(false); @@ -698,10 +702,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Power off, writen 00: Clock to the card is stopped; // D[7:0], CMD, and CK are driven high. - regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); + info.regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); Self { - _peri: sdmmc, + info, + state, + ker_clk, #[cfg(sdmmc_v1)] dma, @@ -726,8 +732,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Data transfer is in progress #[inline] - fn data_active() -> bool { - let regs = T::regs(); + fn data_active(&self) -> bool { + let regs = self.info.regs; let status = regs.star().read(); #[cfg(sdmmc_v1)] @@ -738,8 +744,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Coammand transfer is in progress #[inline] - fn cmd_active() -> bool { - let regs = T::regs(); + fn cmd_active(&self) -> bool { + let regs = self.info.regs; let status = regs.star().read(); #[cfg(sdmmc_v1)] @@ -750,8 +756,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) #[inline] - fn wait_idle() { - while Self::data_active() || Self::cmd_active() {} + fn wait_idle(&self) { + while self.data_active() || self.cmd_active() {} } /// # Safety @@ -759,6 +765,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// `buffer` must be valid for the whole transfer and word aligned #[allow(unused_variables)] fn prepare_datapath_read<'a>( + &self, config: &Config, #[cfg(sdmmc_v1)] dma: &'a mut ChannelAndRequest<'d>, buffer: &'a mut [u32], @@ -766,11 +773,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { block_size: u8, ) -> Transfer<'a> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = T::regs(); + let regs = self.info.regs; // Command AND Data state machines must be idle - Self::wait_idle(); - Self::clear_interrupt_flags(); + self.wait_idle(); + self.clear_interrupt_flags(); regs.dlenr().write(|w| w.set_datalength(length_bytes)); @@ -801,13 +808,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - fn prepare_datapath_write<'a>(&'a mut self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { + fn prepare_datapath_write<'a>(&self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = T::regs(); + let regs = self.info.regs; // Command AND Data state machines must be idle - Self::wait_idle(); - Self::clear_interrupt_flags(); + self.wait_idle(); + self.clear_interrupt_flags(); regs.dlenr().write(|w| w.set_datalength(length_bytes)); @@ -839,8 +846,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } /// Stops the DMA datapath - fn stop_datapath() { - let regs = T::regs(); + fn stop_datapath(&self) { + let regs = self.info.regs; #[cfg(sdmmc_v1)] regs.dctrl().modify(|w| { @@ -853,7 +860,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Sets the CLKDIV field in CLKCR. Updates clock field in self fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { - let regs = T::regs(); + let regs = self.info.regs; let width_u32 = match width { BusWidth::One => 1u32, @@ -862,17 +869,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { _ => panic!("Invalid Bus Width"), }; - let ker_ck = T::frequency(); - let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; + let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq)?; // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 // Section 55.5.8 let sdmmc_bus_bandwidth = new_clock.0 * width_u32; - assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); + assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32); self.clock = new_clock; // CPSMACT and DPSMACT must be 0 to set CLKDIV - Self::wait_idle(); + self.wait_idle(); regs.clkcr().modify(|w| { w.set_clkdiv(clkdiv); #[cfg(sdmmc_v1)] @@ -887,10 +893,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { where CardStatus: From, { - let regs = T::regs(); + let regs = self.info.regs; let rca = card.get_address(); - Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 + self.cmd(common_cmd::card_status(rca, false), false)?; // CMD13 let r1 = regs.respr(0).read().cardstatus(); Ok(r1.into()) @@ -904,7 +910,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Determine Relative Card Address (RCA) of given card let rca = rca.unwrap_or(0); - let r = Self::cmd(common_cmd::select_card(rca), false); + let r = self.cmd(common_cmd::select_card(rca), false); match (r, rca) { (Err(Error::Timeout), 0) => Ok(()), _ => r, @@ -913,8 +919,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Clear flags in interrupt clear register #[inline] - fn clear_interrupt_flags() { - let regs = T::regs(); + fn clear_interrupt_flags(&self) { + let regs = self.info.regs; regs.icr().write(|w| { w.set_ccrcfailc(true); w.set_dcrcfailc(true); @@ -947,12 +953,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Send command to card #[allow(unused_variables)] - fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { - let regs = T::regs(); + fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + let regs = self.info.regs; - Self::clear_interrupt_flags(); + self.clear_interrupt_flags(); // CP state machine must be idle - while Self::cmd_active() {} + while self.cmd_active() {} // Command arg regs.argr().write(|w| w.set_cmdarg(cmd.arg)); @@ -997,13 +1003,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Ok(()) } - fn on_drop() { - let regs = T::regs(); - if Self::data_active() { - Self::clear_interrupt_flags(); + fn on_drop(&self) { + let regs = self.info.regs; + if self.data_active() { + self.clear_interrupt_flags(); // Send abort // CP state machine must be idle - while Self::cmd_active() {} + while self.cmd_active() {} // Command arg regs.argr().write(|w| w.set_cmdarg(0)); @@ -1023,22 +1029,22 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }); // Wait for the abort - while Self::data_active() {} + while self.data_active() {} } regs.maskr().write(|_| ()); // disable irqs - Self::clear_interrupt_flags(); - Self::stop_datapath(); + self.clear_interrupt_flags(); + self.stop_datapath(); } /// Wait for a previously started datapath transfer to complete from an interrupt. #[inline] #[allow(unused)] - async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { + async fn complete_datapath_transfer(&self, block: bool) -> Result<(), Error> { let res = poll_fn(|cx| { // Compiler might not be sufficiently constrained here // https://github.com/embassy-rs/embassy/issues/4723 - T::state().register(cx.waker()); - let status = T::regs().star().read(); + self.state.waker.register(cx.waker()); + let status = self.info.regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); @@ -1067,7 +1073,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }) .await; - Self::clear_interrupt_flags(); + self.clear_interrupt_flags(); res } @@ -1075,7 +1081,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Read a data block. #[inline] pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); let card_capacity = self.card()?.get_capacity(); // NOTE(unsafe) DataBlock uses align 4 @@ -1087,11 +1093,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = Self::prepare_datapath_read( + let transfer = self.prepare_datapath_read( &self.config, #[cfg(sdmmc_v1)] &mut self.dma, @@ -1099,14 +1105,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 512, 9, ); - InterruptHandler::::enable_interrupts(); - Self::cmd(common_cmd::read_single_block(address), true)?; + self.enable_interrupts(); + self.cmd(common_cmd::read_single_block(address), true)?; - let res = Self::complete_datapath_transfer(true).await; + let res = self.complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); } res @@ -1115,7 +1121,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Read multiple data blocks. #[inline] pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); let card_capacity = self.card()?.get_capacity(); // NOTE(unsafe) reinterpret buffer as &mut [u32] @@ -1131,11 +1137,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = Self::prepare_datapath_read( + let transfer = self.prepare_datapath_read( &self.config, #[cfg(sdmmc_v1)] &mut self.dma, @@ -1143,18 +1149,18 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 512 * blocks.len() as u32, 9, ); - InterruptHandler::::enable_interrupts(); + self.enable_interrupts(); - Self::cmd(common_cmd::read_multiple_blocks(address), true)?; + self.cmd(common_cmd::read_multiple_blocks(address), true)?; - let res = Self::complete_datapath_transfer(false).await; + let res = self.complete_datapath_transfer(false).await; - Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 - Self::clear_interrupt_flags(); + self.cmd(common_cmd::stop_transmission(), false)?; // CMD12 + self.clear_interrupt_flags(); if res.is_ok() { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); } res @@ -1162,7 +1168,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Write a data block. pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); let card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) DataBlock uses align 4 @@ -1174,26 +1180,26 @@ impl<'d, T: Instance> Sdmmc<'d, T> { CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] - Self::cmd(common_cmd::write_single_block(address), true)?; + self.cmd(common_cmd::write_single_block(address), true)?; let transfer = self.prepare_datapath_write(buffer, 512, 9); - InterruptHandler::::enable_interrupts(); + self.enable_interrupts(); #[cfg(sdmmc_v2)] - Self::cmd(common_cmd::write_single_block(address), true)?; + self.cmd(common_cmd::write_single_block(address), true)?; - let res = Self::complete_datapath_transfer(true).await; + let res = self.complete_datapath_transfer(true).await; match res { Ok(_) => { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); // TODO: Make this configurable @@ -1219,7 +1225,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Write multiple data blocks. pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); let card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) reinterpret buffer as &[u32] @@ -1236,31 +1242,31 @@ impl<'d, T: Instance> Sdmmc<'d, T> { _ => block_idx, }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 let block_count = blocks.len(); - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); #[cfg(sdmmc_v1)] - Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 + self.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 // Setup write command let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); - InterruptHandler::::enable_interrupts(); + self.enable_interrupts(); #[cfg(sdmmc_v2)] - Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 + self.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 - let res = Self::complete_datapath_transfer(false).await; + let res = self.complete_datapath_transfer(false).await; - Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 - Self::clear_interrupt_flags(); + self.cmd(common_cmd::stop_transmission(), false)?; // CMD12 + self.clear_interrupt_flags(); match res { Ok(_) => { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); // TODO: Make this configurable @@ -1306,8 +1312,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { - let regs = T::regs(); - let ker_ck = T::frequency(); + let regs = self.info.regs; let bus_width = match (self.d3.is_some(), self.d7.is_some()) { (true, true) => { @@ -1322,11 +1327,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // While the SD/SDIO card or eMMC is in identification mode, // the SDMMC_CK frequency must be no more than 400 kHz. - let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); + let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(self.ker_clk, SD_INIT_FREQ.0)); self.clock = init_clock; // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); + self.wait_idle(); regs.clkcr().modify(|w| { w.set_widbus(0); @@ -1338,12 +1343,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { .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)?; + self.cmd(common_cmd::idle(), false)?; match card { SdmmcPeripheral::SdCard(ref mut card) => { // Check if cards supports CMD8 (with pattern) - Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; + self.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; let cic = CIC::from(regs.respr(0).read().cardstatus()); if cic.pattern() != 0xAA { @@ -1356,12 +1361,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let ocr = loop { // Signal that next command is a app command - Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 + self.cmd(common_cmd::app_cmd(0), false)?; // CMD55 // 3.2-3.3V let voltage_window = 1 << 5; // Initialize card - match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { + match self.cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { // ACMD41 Ok(_) => (), Err(Error::Crc) => (), @@ -1388,7 +1393,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let access_mode = 0b10 << 29; let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; // Initialize card - match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { + match self.cmd(emmc_cmd::send_op_cond(op_cond), false) { Ok(_) => (), Err(Error::Crc) => (), Err(err) => return Err(err), @@ -1410,7 +1415,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } } - Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 + self.cmd(common_cmd::all_send_cid(), false)?; // CMD2 let cid0 = regs.respr(0).read().cardstatus() as u128; let cid1 = regs.respr(1).read().cardstatus() as u128; let cid2 = regs.respr(2).read().cardstatus() as u128; @@ -1421,7 +1426,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { SdmmcPeripheral::SdCard(ref mut card) => { card.cid = cid.into(); - Self::cmd(sd_cmd::send_relative_address(), false)?; + self.cmd(sd_cmd::send_relative_address(), false)?; let rca = RCA::::from(regs.respr(0).read().cardstatus()); card.rca = rca.address(); } @@ -1429,11 +1434,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { emmc.cid = cid.into(); emmc.rca = 1u16.into(); - Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; + self.cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; } } - Self::cmd(common_cmd::send_csd(card.get_address()), false)?; + self.cmd(common_cmd::send_csd(card.get_address()), false)?; let csd0 = regs.respr(0).read().cardstatus() as u128; let csd1 = regs.respr(1).read().cardstatus() as u128; let csd2 = regs.respr(2).read().cardstatus() as u128; @@ -1475,12 +1480,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { BusWidth::Four if card.scr.bus_width_four() => 2, _ => 0, }; - Self::cmd(common_cmd::app_cmd(card.rca), false)?; - Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; + self.cmd(common_cmd::app_cmd(card.rca), false)?; + self.cmd(sd_cmd::cmd6(acmd_arg), false)?; } SdmmcPeripheral::Emmc(_) => { // Write bus width to ExtCSD byte 183 - Self::cmd( + self.cmd( emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), false, )?; @@ -1497,7 +1502,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); + self.wait_idle(); regs.clkcr().modify(|w| w.set_widbus(widbus)); @@ -1546,7 +1551,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// /// SD only. pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await } @@ -1579,9 +1584,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }; // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = Self::prepare_datapath_read( + let transfer = self.prepare_datapath_read( &self.config, #[cfg(sdmmc_v1)] &mut self.dma, @@ -1589,10 +1594,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 64, 6, ); - InterruptHandler::::enable_interrupts(); - Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 + self.enable_interrupts(); + self.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - let res = Self::complete_datapath_transfer(true).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 @@ -1605,7 +1610,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { match res { Ok(_) => { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); // Function Selection of Function Group 1 @@ -1629,8 +1634,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// SD only. async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { // Read the 64-bit SCR register - Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 - Self::cmd(common_cmd::app_cmd(card.rca), false)?; + self.cmd(common_cmd::set_block_length(8), false)?; // CMD16 + self.cmd(common_cmd::app_cmd(card.rca), false)?; let cmd_block = match self.cmd_block.as_deref_mut() { Some(x) => x, @@ -1639,9 +1644,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let scr = &mut cmd_block.0[..2]; // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = Self::prepare_datapath_read( + let transfer = self.prepare_datapath_read( &self.config, #[cfg(sdmmc_v1)] &mut self.dma, @@ -1649,14 +1654,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 8, 3, ); - InterruptHandler::::enable_interrupts(); - Self::cmd(sd_cmd::send_scr(), true)?; + self.enable_interrupts(); + self.cmd(sd_cmd::send_scr(), true)?; - let res = Self::complete_datapath_transfer(true).await; + let res = self.complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); unsafe { @@ -1679,15 +1684,15 @@ impl<'d, T: Instance> Sdmmc<'d, T> { None => &mut CmdBlock::new(), }; - Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 - Self::cmd(common_cmd::app_cmd(rca), false)?; // APP + self.cmd(common_cmd::set_block_length(64), false)?; // CMD16 + self.cmd(common_cmd::app_cmd(rca), false)?; // APP let status = cmd_block; // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| Self::on_drop()); + let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = Self::prepare_datapath_read( + let transfer = self.prepare_datapath_read( &self.config, #[cfg(sdmmc_v1)] &mut self.dma, @@ -1695,14 +1700,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 64, 6, ); - InterruptHandler::::enable_interrupts(); - Self::cmd(sd_cmd::sd_status(), true)?; + self.enable_interrupts(); + self.cmd(sd_cmd::sd_status(), true)?; - let res = Self::complete_datapath_transfer(true).await; + let res = self.complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); for byte in status.iter_mut() { @@ -1717,7 +1722,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// /// eMMC only. pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await } @@ -1726,7 +1731,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// /// eMMC only. async fn read_ext_csd(&mut self) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?.get_emmc(); + let mut card = self.card.take().ok_or(Error::NoCard)?; + let emmc = card.get_emmc(); // Note: cmd_block can't be used because ExtCSD is too long to fit. let mut data_block = DataBlock([0u8; 512]); @@ -1734,12 +1740,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // NOTE(unsafe) DataBlock uses align 4 let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; - Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 + self.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| Self::on_drop()); - - let transfer = Self::prepare_datapath_read( + let transfer = self.prepare_datapath_read( &self.config, #[cfg(sdmmc_v1)] &mut self.dma, @@ -1747,26 +1750,30 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 512, 9, ); - InterruptHandler::::enable_interrupts(); - Self::cmd(emmc_cmd::send_ext_csd(), true)?; + self.enable_interrupts(); + self.cmd(emmc_cmd::send_ext_csd(), true)?; - let res = Self::complete_datapath_transfer(true).await; + // Arm `OnDrop` after the buffer, so it will be dropped first + let on_drop = OnDrop::new(|| self.on_drop()); + + let res = self.complete_datapath_transfer(true).await; if res.is_ok() { on_drop.defuse(); - Self::stop_datapath(); + self.stop_datapath(); drop(transfer); - card.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); + emmc.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); } + res } } -impl<'d, T: Instance> Drop for Sdmmc<'d, T> { +impl<'d> Drop for Sdmmc<'d> { fn drop(&mut self) { - T::Interrupt::disable(); - Self::on_drop(); + // T::Interrupt::disable(); + self.on_drop(); critical_section::with(|_| { self.clk.set_as_disconnected(); @@ -1799,9 +1806,28 @@ impl<'d, T: Instance> Drop for Sdmmc<'d, T> { ////////////////////////////////////////////////////// +type Regs = RegBlock; + +struct Info { + regs: Regs, + rcc: RccInfo, +} + +struct State { + waker: AtomicWaker, +} + +impl State { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } +} + trait SealedInstance { - fn regs() -> RegBlock; - fn state() -> &'static AtomicWaker; + fn info() -> &'static Info; + fn state() -> &'static State; } /// SDMMC instance trait. @@ -1828,13 +1854,17 @@ dma_trait!(SdmmcDma, Instance); foreach_peripheral!( (sdmmc, $inst:ident) => { impl SealedInstance for peripherals::$inst { - fn regs() -> RegBlock { - crate::pac::$inst + fn info() -> &'static Info { + static INFO: Info = Info { + regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }, + rcc: crate::peripherals::$inst::RCC_INFO, + }; + &INFO } - fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { - static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new(); - &WAKER + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE } } @@ -1844,7 +1874,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { +impl<'d> block_device_driver::BlockDevice<512> for Sdmmc<'d> { type Error = Error; type Align = aligned::A4; @@ -1853,7 +1883,7 @@ impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { block_address: u32, buf: &mut [aligned::Aligned], ) -> Result<(), Self::Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); // TODO: I think block_address needs to be adjusted by the partition start offset if buf.len() == 1 { let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; @@ -1871,7 +1901,7 @@ impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { block_address: u32, buf: &[aligned::Aligned], ) -> Result<(), Self::Error> { - let _scoped_block_stop = T::RCC_INFO.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); // TODO: I think block_address needs to be adjusted by the partition start offset if buf.len() == 1 { let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; -- cgit From 142c237b781daf0f5bc2ac6e165d14454b87544c Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 9 Dec 2025 11:35:37 -0600 Subject: sdmmc: use storage devices with reference --- embassy-stm32/src/sdmmc/mod.rs | 718 +++++++++++++++++++++++------------------ embassy-stm32/src/time.rs | 2 +- 2 files changed, 403 insertions(+), 317 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 74a6f13fa..f862d73b1 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -9,7 +9,6 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; -use embassy_sync::mutex::Mutex; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::common_cmd::{self, Resp, ResponseLen}; use sdio_host::emmc::{EMMC, ExtCSD}; @@ -165,6 +164,55 @@ pub enum Error { StBitErr, } +pub trait Addressable: Sized { + type Ext; + + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16; + + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity; + + /// Size in bytes + fn size(&self) -> u64; + + async fn write_block<'a>( + &mut self, + sdmmc: &mut Sdmmc<'a>, + block_idx: u32, + buffer: &DataBlock, + ) -> Result<(), Error> { + sdmmc.write_block(self, block_idx, buffer).await + } + + async fn write_blocks<'a>( + &mut self, + sdmmc: &mut Sdmmc<'a>, + block_idx: u32, + blocks: &[DataBlock], + ) -> Result<(), Error> { + sdmmc.write_blocks(self, block_idx, blocks).await + } + + async fn read_block<'a>( + &mut self, + sdmmc: &mut Sdmmc<'a>, + block_idx: u32, + buffer: &mut DataBlock, + ) -> Result<(), Error> { + sdmmc.read_block(self, block_idx, buffer).await + } + + async fn read_blocks<'a>( + &mut self, + sdmmc: &mut Sdmmc<'a>, + block_idx: u32, + blocks: &mut [DataBlock], + ) -> Result<(), Error> { + sdmmc.read_blocks(self, block_idx, blocks).await + } +} + #[derive(Clone, Copy, Debug, Default)] /// SD Card pub struct Card { @@ -184,6 +232,178 @@ pub struct Card { pub status: SDStatus, } +impl Card { + /// Switch mode using CMD6. + /// + /// Attempt to set a new signalling mode. The selected + /// signalling mode is returned. Expects the current clock + /// frequency to be > 12.5MHz. + /// + /// SD only. + pub async fn switch_signalling_mode<'a>( + &mut self, + sdmmc: &mut Sdmmc<'a>, + cmd_block: &mut CmdBlock, + signalling: Signalling, + ) -> Result { + // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not + // necessary" + + let set_function = 0x8000_0000 + | match signalling { + // See PLSS v7_10 Table 4-11 + Signalling::DDR50 => 0xFF_FF04, + Signalling::SDR104 => 0xFF_1F03, + Signalling::SDR50 => 0xFF_1F02, + Signalling::SDR25 => 0xFF_FF01, + Signalling::SDR12 => 0xFF_FF00, + }; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let on_drop = OnDrop::new(|| sdmmc.on_drop()); + + let transfer = sdmmc.prepare_datapath_read( + &sdmmc.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + cmd_block.as_mut(), + 64, + 6, + ); + sdmmc.enable_interrupts(); + sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 + + let res = sdmmc.complete_datapath_transfer(true).await; + + // Host is allowed to use the new functions at least 8 + // clocks after the end of the switch command + // transaction. We know the current clock period is < 80ns, + // so a total delay of 640ns is required here + for _ in 0..300 { + cortex_m::asm::nop(); + } + + match res { + Ok(_) => { + on_drop.defuse(); + sdmmc.stop_datapath(); + drop(transfer); + + // Function Selection of Function Group 1 + let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; + + match selection { + 0 => Ok(Signalling::SDR12), + 1 => Ok(Signalling::SDR25), + 2 => Ok(Signalling::SDR50), + 3 => Ok(Signalling::SDR104), + 4 => Ok(Signalling::DDR50), + _ => Err(Error::UnsupportedCardType), + } + } + Err(e) => Err(e), + } + } + + /// Reads the SCR register. + /// + /// SD only. + pub async fn get_scr<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { + // Read the 64-bit SCR register + sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 + sdmmc.cmd(common_cmd::app_cmd(self.rca), false)?; + + let scr = &mut cmd_block.0[..2]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let on_drop = OnDrop::new(|| sdmmc.on_drop()); + + let transfer = sdmmc.prepare_datapath_read( + &sdmmc.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + scr, + 8, + 3, + ); + sdmmc.enable_interrupts(); + sdmmc.cmd(sd_cmd::send_scr(), true)?; + + let res = sdmmc.complete_datapath_transfer(true).await; + + if res.is_ok() { + on_drop.defuse(); + sdmmc.stop_datapath(); + drop(transfer); + + unsafe { + let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); + self.scr = SCR(u64::from_be_bytes(*scr_bytes)); + } + } + res + } + + /// Reads the SD Status (ACMD13) + /// + /// SD only. + pub async fn read_sd_status<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { + let rca = self.rca; + + sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 + sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP + + let status = cmd_block; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let on_drop = OnDrop::new(|| sdmmc.on_drop()); + + let transfer = sdmmc.prepare_datapath_read( + &sdmmc.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + status.as_mut(), + 64, + 6, + ); + sdmmc.enable_interrupts(); + sdmmc.cmd(sd_cmd::sd_status(), true)?; + + let res = sdmmc.complete_datapath_transfer(true).await; + + if res.is_ok() { + on_drop.defuse(); + sdmmc.stop_datapath(); + drop(transfer); + + for byte in status.iter_mut() { + *byte = u32::from_be(*byte); + } + self.status = status.0.into(); + } + res + } +} + +impl Addressable for Card { + type Ext = SD; + + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16 { + self.rca + } + + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity { + self.card_type + } + + /// Size in bytes + fn size(&self) -> u64 { + u64::from(self.csd.block_count()) * 512 + } +} + #[derive(Clone, Copy, Debug, Default)] /// eMMC storage pub struct Emmc { @@ -201,6 +421,66 @@ pub struct Emmc { pub ext_csd: ExtCSD, } +impl Emmc { + /// Gets the EXT_CSD register. + /// + /// eMMC only. + async fn read_ext_csd<'a>(&mut self, sdmmc: &mut Sdmmc<'a>) -> Result<(), Error> { + // Note: cmd_block can't be used because ExtCSD is too long to fit. + let mut data_block = DataBlock([0u8; 512]); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; + + sdmmc.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 + + let transfer = sdmmc.prepare_datapath_read( + &sdmmc.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + buffer, + 512, + 9, + ); + sdmmc.enable_interrupts(); + sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let on_drop = OnDrop::new(|| sdmmc.on_drop()); + + let res = sdmmc.complete_datapath_transfer(true).await; + + if res.is_ok() { + on_drop.defuse(); + sdmmc.stop_datapath(); + drop(transfer); + + self.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); + } + + res + } +} + +impl Addressable for Emmc { + type Ext = EMMC; + + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16 { + self.rca + } + + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity { + self.capacity + } + + /// Size in bytes + fn size(&self) -> u64 { + u64::from(self.ext_csd.sector_count()) * 512 + } +} + #[repr(u8)] enum PowerCtrl { Off = 0b00, @@ -386,12 +666,6 @@ pub struct Sdmmc<'d> { clock: Hertz, /// Current signalling scheme to card signalling: Signalling, - /// Card - card: Option, - - /// An optional buffer to be used for commands - /// This should be used if there are special memory location requirements for dma - cmd_block: Option<&'d mut CmdBlock>, } const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); @@ -725,8 +999,6 @@ impl<'d> Sdmmc<'d> { config, clock: SD_INIT_FREQ, signalling: Default::default(), - card: None, - cmd_block: None, } } @@ -889,9 +1161,9 @@ impl<'d> Sdmmc<'d> { } /// Query the card status (CMD13, returns R1) - fn read_status(&self, card: &SdmmcPeripheral) -> Result, Error> + fn read_status(&self, card: &A) -> Result, Error> where - CardStatus: From, + CardStatus: From, { let regs = self.info.regs; let rca = card.get_address(); @@ -1080,9 +1352,14 @@ impl<'d> Sdmmc<'d> { /// 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, + card: &mut impl Addressable, + block_idx: u32, + buffer: &mut DataBlock, + ) -> Result<(), Error> { let _scoped_block_stop = self.info.rcc.block_stop(); - let card_capacity = self.card()?.get_capacity(); + let card_capacity = card.get_capacity(); // NOTE(unsafe) DataBlock uses align 4 let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; @@ -1120,9 +1397,14 @@ impl<'d> Sdmmc<'d> { /// Read multiple data blocks. #[inline] - pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { + pub async fn read_blocks( + &mut self, + card: &mut impl Addressable, + block_idx: u32, + blocks: &mut [DataBlock], + ) -> Result<(), Error> { let _scoped_block_stop = self.info.rcc.block_stop(); - let card_capacity = self.card()?.get_capacity(); + let card_capacity = card.get_capacity(); // NOTE(unsafe) reinterpret buffer as &mut [u32] let buffer = unsafe { @@ -1167,9 +1449,16 @@ impl<'d> Sdmmc<'d> { } /// Write a data block. - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + pub async fn write_block( + &mut self, + card: &mut A, + block_idx: u32, + buffer: &DataBlock, + ) -> Result<(), Error> + where + CardStatus: From, + { let _scoped_block_stop = self.info.rcc.block_stop(); - let card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) DataBlock uses align 4 let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; @@ -1205,13 +1494,8 @@ impl<'d> Sdmmc<'d> { // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; - let card = self.card.as_ref().unwrap(); while timeout > 0 { - let ready_for_data = match card { - SdmmcPeripheral::Emmc(_) => self.read_status::(card)?.ready_for_data(), - SdmmcPeripheral::SdCard(_) => self.read_status::(card)?.ready_for_data(), - }; - + let ready_for_data = self.read_status(card)?.ready_for_data(); if ready_for_data { return Ok(()); } @@ -1224,9 +1508,16 @@ impl<'d> Sdmmc<'d> { } /// Write multiple data blocks. - pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { + pub async fn write_blocks( + &mut self, + card: &mut A, + block_idx: u32, + blocks: &[DataBlock], + ) -> Result<(), Error> + where + CardStatus: From, + { let _scoped_block_stop = self.info.rcc.block_stop(); - let card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) reinterpret buffer as &[u32] let buffer = unsafe { @@ -1272,12 +1563,11 @@ impl<'d> Sdmmc<'d> { // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; - // Try to read card status (ACMD13) while timeout > 0 { - match self.read_sd_status().await { - Ok(_) => return Ok(()), - Err(Error::Timeout) => (), // Try again - Err(e) => return Err(e), + let ready_for_data = self.read_status(card)?.ready_for_data(); + + if ready_for_data { + return Ok(()); } timeout -= 1; } @@ -1287,31 +1577,17 @@ impl<'d> Sdmmc<'d> { } } - /// Get a reference to the initialized card - /// - /// # Errors - /// - /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or - /// [`init_emmc`](#method.init_emmc) has not previously succeeded - #[inline] - pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { - self.card.as_ref().ok_or(Error::NoCard) - } - /// Get the current SDMMC bus clock pub fn clock(&self) -> Hertz { self.clock } - /// Set a specific cmd buffer rather than using the default stack allocated one. - /// This is required if stack RAM cannot be used with DMA and usually manifests - /// itself as an indefinite wait on a dma transfer because the dma peripheral - /// cannot access the memory. - pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { - self.cmd_block = Some(cmd_block) - } - - async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { + async fn init_internal( + &mut self, + cmd_block: &mut CmdBlock, + freq: Hertz, + card: &mut SdmmcPeripheral, + ) -> Result<(), Error> { let regs = self.info.regs; let bus_width = match (self.d3.is_some(), self.d7.is_some()) { @@ -1346,7 +1622,7 @@ impl<'d> Sdmmc<'d> { self.cmd(common_cmd::idle(), false)?; match card { - SdmmcPeripheral::SdCard(ref mut card) => { + SdmmcPeripheral::SdCard(card) => { // Check if cards supports CMD8 (with pattern) self.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; let cic = CIC::from(regs.respr(0).read().cardstatus()); @@ -1387,7 +1663,7 @@ impl<'d> Sdmmc<'d> { } card.ocr = ocr; } - SdmmcPeripheral::Emmc(ref mut emmc) => { + SdmmcPeripheral::Emmc(emmc) => { let ocr = loop { let high_voltage = 0b0 << 7; let access_mode = 0b10 << 29; @@ -1423,14 +1699,14 @@ impl<'d> Sdmmc<'d> { let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); match card { - SdmmcPeripheral::SdCard(ref mut card) => { + SdmmcPeripheral::SdCard(card) => { card.cid = cid.into(); self.cmd(sd_cmd::send_relative_address(), false)?; let rca = RCA::::from(regs.respr(0).read().cardstatus()); card.rca = rca.address(); } - SdmmcPeripheral::Emmc(ref mut emmc) => { + SdmmcPeripheral::Emmc(emmc) => { emmc.cid = cid.into(); emmc.rca = 1u16.into(); @@ -1448,10 +1724,10 @@ impl<'d> Sdmmc<'d> { self.select_card(Some(card.get_address()))?; let bus_width = match card { - SdmmcPeripheral::SdCard(ref mut card) => { + SdmmcPeripheral::SdCard(card) => { card.csd = csd.into(); - self.get_scr(card).await?; + card.get_scr(self, cmd_block).await?; if !card.scr.bus_width_four() { BusWidth::One @@ -1459,7 +1735,7 @@ impl<'d> Sdmmc<'d> { BusWidth::Four } } - SdmmcPeripheral::Emmc(ref mut emmc) => { + SdmmcPeripheral::Emmc(emmc) => { emmc.csd = csd.into(); bus_width @@ -1475,7 +1751,7 @@ impl<'d> Sdmmc<'d> { }; match card { - SdmmcPeripheral::SdCard(ref mut card) => { + SdmmcPeripheral::SdCard(card) => { let acmd_arg = match bus_width { BusWidth::Four if card.scr.bus_width_four() => 2, _ => 0, @@ -1483,7 +1759,7 @@ impl<'d> Sdmmc<'d> { self.cmd(common_cmd::app_cmd(card.rca), false)?; self.cmd(sd_cmd::cmd6(acmd_arg), false)?; } - SdmmcPeripheral::Emmc(_) => { + SdmmcPeripheral::Emmc(emmc) => { // Write bus width to ExtCSD byte 183 self.cmd( emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), @@ -1492,7 +1768,7 @@ impl<'d> Sdmmc<'d> { // Wait for ready after R1b response loop { - let status = self.read_status::(&card)?; + let status = self.read_status(emmc)?; if status.ready_for_data() { break; @@ -1515,32 +1791,30 @@ impl<'d> Sdmmc<'d> { self.clkcr_set_clkdiv(25_000_000, bus_width)?; } - self.card = Some(card); - match card { - SdmmcPeripheral::SdCard(_) => { + SdmmcPeripheral::SdCard(card) => { // Read status - self.read_sd_status().await?; + card.read_sd_status(self, cmd_block).await?; if freq.0 > 25_000_000 { // Switch to SDR25 - self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; + self.signalling = card.switch_signalling_mode(self, cmd_block, Signalling::SDR25).await?; if self.signalling == Signalling::SDR25 { // Set final clock frequency self.clkcr_set_clkdiv(freq.0, bus_width)?; - if self.read_status::(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { + if self.read_status(card)?.state() != CurrentState::Transfer { return Err(Error::SignalingSwitchFailed); } } } // Read status after signalling change - self.read_sd_status().await?; + card.read_sd_status(self, cmd_block).await?; } - SdmmcPeripheral::Emmc(_) => { - self.read_ext_csd().await?; + SdmmcPeripheral::Emmc(emmc) => { + emmc.read_ext_csd(self).await?; } } @@ -1550,223 +1824,35 @@ impl<'d> Sdmmc<'d> { /// Initializes card (if present) and sets the bus at the specified frequency. /// /// SD only. - pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { + pub async fn init_sd_card(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { let _scoped_block_stop = self.info.rcc.block_stop(); - self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await - } - - /// Switch mode using CMD6. - /// - /// Attempt to set a new signalling mode. The selected - /// signalling mode is returned. Expects the current clock - /// frequency to be > 12.5MHz. - /// - /// SD only. - async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result { - let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); - // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not - // necessary" - - let set_function = 0x8000_0000 - | match signalling { - // See PLSS v7_10 Table 4-11 - Signalling::DDR50 => 0xFF_FF04, - Signalling::SDR104 => 0xFF_1F03, - Signalling::SDR50 => 0xFF_1F02, - Signalling::SDR25 => 0xFF_FF01, - Signalling::SDR12 => 0xFF_FF00, - }; - - let status = match self.cmd_block.as_deref_mut() { - Some(x) => x, - None => &mut CmdBlock::new(), - }; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.on_drop()); - - let transfer = self.prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - status.as_mut(), - 64, - 6, - ); - self.enable_interrupts(); - self.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - - 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 - // transaction. We know the current clock period is < 80ns, - // so a total delay of 640ns is required here - for _ in 0..300 { - cortex_m::asm::nop(); - } - - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - - // Function Selection of Function Group 1 - let selection = (u32::from_be(status[4]) >> 24) & 0xF; - - match selection { - 0 => Ok(Signalling::SDR12), - 1 => Ok(Signalling::SDR25), - 2 => Ok(Signalling::SDR50), - 3 => Ok(Signalling::SDR104), - 4 => Ok(Signalling::DDR50), - _ => Err(Error::UnsupportedCardType), - } - } - Err(e) => Err(e), - } - } - - /// Reads the SCR register. - /// - /// SD only. - async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { - // Read the 64-bit SCR register - self.cmd(common_cmd::set_block_length(8), false)?; // CMD16 - self.cmd(common_cmd::app_cmd(card.rca), false)?; - - let cmd_block = match self.cmd_block.as_deref_mut() { - Some(x) => x, - None => &mut CmdBlock::new(), - }; - let scr = &mut cmd_block.0[..2]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.on_drop()); - - let transfer = self.prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - scr, - 8, - 3, - ); - self.enable_interrupts(); - self.cmd(sd_cmd::send_scr(), true)?; + let mut card = SdmmcPeripheral::SdCard(Card::default()); + self.init_internal(cmd_block, freq, &mut card).await?; - let res = self.complete_datapath_transfer(true).await; - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - - unsafe { - let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); - card.scr = SCR(u64::from_be_bytes(*scr_bytes)); - } - } - res - } - - /// Reads the SD Status (ACMD13) - /// - /// SD only. - async fn read_sd_status(&mut self) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); - let rca = card.rca; - - let cmd_block = match self.cmd_block.as_deref_mut() { - Some(x) => x, - None => &mut CmdBlock::new(), + let card = match card { + SdmmcPeripheral::SdCard(card) => card, + _ => unreachable!(), }; - self.cmd(common_cmd::set_block_length(64), false)?; // CMD16 - self.cmd(common_cmd::app_cmd(rca), false)?; // APP - - let status = cmd_block; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.on_drop()); - - let transfer = self.prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - status.as_mut(), - 64, - 6, - ); - self.enable_interrupts(); - self.cmd(sd_cmd::sd_status(), true)?; - - let res = self.complete_datapath_transfer(true).await; - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); - } - card.status = status.0.into(); - } - res + Ok(card) } /// Initializes eMMC and sets the bus at the specified frequency. /// /// eMMC only. - pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { + pub async fn init_emmc(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { let _scoped_block_stop = self.info.rcc.block_stop(); - self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await - } - - /// Gets the EXT_CSD register. - /// - /// eMMC only. - async fn read_ext_csd(&mut self) -> Result<(), Error> { - let mut card = self.card.take().ok_or(Error::NoCard)?; - let emmc = card.get_emmc(); - - // Note: cmd_block can't be used because ExtCSD is too long to fit. - let mut data_block = DataBlock([0u8; 512]); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; - - self.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 - - let transfer = self.prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - buffer, - 512, - 9, - ); - self.enable_interrupts(); - self.cmd(emmc_cmd::send_ext_csd(), true)?; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.on_drop()); - - let res = self.complete_datapath_transfer(true).await; - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); + let mut card = SdmmcPeripheral::Emmc(Emmc::default()); + self.init_internal(cmd_block, freq, &mut card).await?; - emmc.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); - } + let card = match card { + SdmmcPeripheral::Emmc(card) => card, + _ => unreachable!(), + }; - res + Ok(card) } } @@ -1874,47 +1960,47 @@ foreach_peripheral!( }; ); -impl<'d> block_device_driver::BlockDevice<512> for Sdmmc<'d> { - type Error = Error; - type Align = aligned::A4; - - async fn read( - &mut self, - block_address: u32, - buf: &mut [aligned::Aligned], - ) -> Result<(), Self::Error> { - let _scoped_block_stop = self.info.rcc.block_stop(); - // TODO: I think block_address needs to be adjusted by the partition start offset - if buf.len() == 1 { - let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; - self.read_block(block_address, block).await?; - } else { - let blocks: &mut [DataBlock] = - unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; - self.read_blocks(block_address, blocks).await?; - } - Ok(()) - } - - async fn write( - &mut self, - block_address: u32, - buf: &[aligned::Aligned], - ) -> Result<(), Self::Error> { - let _scoped_block_stop = self.info.rcc.block_stop(); - // TODO: I think block_address needs to be adjusted by the partition start offset - if buf.len() == 1 { - let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; - self.write_block(block_address, block).await?; - } else { - let blocks: &[DataBlock] = - unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; - self.write_blocks(block_address, blocks).await?; - } - Ok(()) - } - - async fn size(&mut self) -> Result { - Ok(self.card()?.size()) - } -} +// impl<'d, A: Addressable> block_device_driver::BlockDevice<512> for Sdmmc<'d>, A { +// type Error = Error; +// type Align = aligned::A4; +// +// async fn read( +// &mut self, +// block_address: u32, +// buf: &mut [aligned::Aligned], +// ) -> Result<(), Self::Error> { +// let _scoped_block_stop = self.info.rcc.block_stop(); +// // TODO: I think block_address needs to be adjusted by the partition start offset +// if buf.len() == 1 { +// let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; +// self.read_block(block_address, block).await?; +// } else { +// let blocks: &mut [DataBlock] = +// unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; +// self.read_blocks(block_address, blocks).await?; +// } +// Ok(()) +// } +// +// async fn write( +// &mut self, +// block_address: u32, +// buf: &[aligned::Aligned], +// ) -> Result<(), Self::Error> { +// let _scoped_block_stop = self.info.rcc.block_stop(); +// // TODO: I think block_address needs to be adjusted by the partition start offset +// if buf.len() == 1 { +// let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; +// self.write_block(block_address, block).await?; +// } else { +// let blocks: &[DataBlock] = +// unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; +// self.write_blocks(block_address, blocks).await?; +// } +// Ok(()) +// } +// +// async fn size(&mut self) -> Result { +// Ok(self.card()?.size()) +// } +// } diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 532877f70..88a28ee3d 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs @@ -4,7 +4,7 @@ use core::fmt::Display; use core::ops::{Div, Mul}; /// Hertz -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)] pub struct Hertz(pub u32); impl Display for Hertz { -- cgit From 1ccf45058db4e77ac2c59357cab196b659201b63 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Fri, 5 Dec 2025 14:37:19 -0800 Subject: ADC driver improvement Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 429 +++++++++++++++++++++++++----- embassy-mcxa/src/clocks/mod.rs | 3 + embassy-mcxa/src/clocks/periph_helpers.rs | 1 + embassy-mcxa/src/interrupt.rs | 122 ++++++++- embassy-mcxa/src/lib.rs | 10 +- embassy-mcxa/src/pins.rs | 33 --- examples/mcxa/src/bin/adc_interrupt.rs | 42 +-- examples/mcxa/src/bin/adc_polling.rs | 18 +- examples/mcxa/src/lib.rs | 16 -- 9 files changed, 519 insertions(+), 155 deletions(-) delete mode 100644 embassy-mcxa/src/pins.rs delete mode 100644 examples/mcxa/src/lib.rs diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 7475299ba..2e2fb0342 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -1,42 +1,29 @@ //! ADC driver -use core::sync::atomic::{AtomicBool, Ordering}; +use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; +use paste::paste; +use crate::pac; +use crate::interrupt::typelevel::{Handler, Interrupt}; +use crate::gpio::{GpioPin, SealedPin}; +use maitake_sync::WaitCell; + use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; use crate::clocks::{Gate, PoweredClock, enable_and_reset}; -use crate::pac; + use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; use crate::pac::adc1::ctrl::CalAvgs; use crate::pac::adc1::tctrl::{Tcmd, Tpri}; -type Regs = pac::adc1::RegisterBlock; +/// Global wait cell for alarm notifications +static WAKER: WaitCell = WaitCell::new(); -static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); -// Token-based instance pattern like embassy-imxrt -pub trait Instance: Gate + PeripheralType { - fn ptr() -> *const Regs; -} - -/// Token for ADC1 -pub type Adc1 = crate::peripherals::ADC1; -impl Instance for crate::peripherals::ADC1 { - #[inline(always)] - fn ptr() -> *const Regs { - pac::Adc1::ptr() - } -} - -// Also implement Instance for the Peri wrapper type -// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { -// #[inline(always)] -// fn ptr() -> *const Regs { -// pac::Adc1::ptr() -// } -// } +const G_LPADC_RESULT_SHIFT: u32 = 0; +/// Trigger priority policy for ADC conversions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum TriggerPriorityPolicy { @@ -52,20 +39,40 @@ pub enum TriggerPriorityPolicy { TriggerPriorityExceptionDisabled = 16, } +/// Configuration for the LPADC peripheral. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LpadcConfig { + /// Control system transition to Stop and Wait power modes while ADC is converting. + /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. + /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. pub enable_in_doze_mode: bool, + /// Auto-Calibration Averages. pub conversion_average_mode: CalAvgs, + /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption). pub enable_analog_preliminary: bool, + /// Power-up delay value (in ADC clock cycles) pub power_up_delay: u8, + /// Reference voltage source selection pub reference_voltage_source: Refsel, + /// Power configuration selection. pub power_level_mode: Pwrsel, + /// Trigger priority policy for handling multiple triggers pub trigger_priority_policy: TriggerPriorityPolicy, + /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, + /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. pub enable_conv_pause: bool, + /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. + /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. pub conv_pause_delay: u16, + /// FIFO watermark level for interrupt generation. + /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field, + /// the ready flag would be asserted to indicate stored data has reached the programmable threshold. pub fifo_watermark: u8, + /// Power configuration (normal/deep sleep behavior) pub power: PoweredClock, + /// ADC clock source selection pub source: AdcClockSel, + /// Clock divider for ADC clock pub div: Div4, } @@ -89,6 +96,9 @@ impl Default for LpadcConfig { } } +/// Configuration for a conversion command. +/// +/// Defines the parameters for a single ADC conversion operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvCommandConfig { pub sample_channel_mode: Ctype, @@ -105,6 +115,10 @@ pub struct ConvCommandConfig { pub enable_wait_trigger: bool, } + +/// Configuration for a conversion trigger. +/// +/// Defines how a trigger initiates ADC conversions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvTriggerConfig { pub target_command_id: Tcmd, @@ -113,6 +127,9 @@ pub struct ConvTriggerConfig { pub enable_hardware_trigger: bool, } +/// Result of an ADC conversion. +/// +/// Contains the conversion value and metadata about the conversion. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvResult { pub command_id_source: u32, @@ -121,14 +138,59 @@ pub struct ConvResult { pub conv_value: u16, } +/// ADC interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +/// ADC driver instance. pub struct Adc<'a, I: Instance> { - _inst: core::marker::PhantomData<&'a mut I>, + _inst: PhantomData<&'a mut I>, } impl<'a, I: Instance> Adc<'a, I> { - /// initialize ADC - pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self { - let adc = unsafe { &*I::ptr() }; + /// Initialize ADC with interrupt support. + /// + /// # Arguments + /// * `_inst` - ADC peripheral instance + /// * `pin` - GPIO pin to use for ADC + /// * `_irq` - Interrupt binding for this ADC instance + /// * `config` - ADC configuration + pub fn new( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + _irq: impl crate::interrupt::typelevel::Binding> + 'a, + config: LpadcConfig, + ) -> Self { + let adc = Self::new_inner(_inst, pin, config); + + I::Interrupt::unpend(); + unsafe { I::Interrupt::enable() }; + + adc + } + + /// Initialize ADC without interrupt support (for polling mode). + /// + /// # Arguments + /// * `_inst` - ADC peripheral instance + /// * `pin` - GPIO pin to use for ADC input + /// * `config` - ADC configuration + pub fn new_polling( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + config: LpadcConfig, + ) -> Self { + Self::new_inner(_inst, pin, config) + } + + /// Internal initialization function shared by `new` and `new_polling`. + fn new_inner( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + config: LpadcConfig, + ) -> Self { + let adc = &*I::ptr(); let _clock_freq = unsafe { enable_and_reset::(&AdcConfig { @@ -139,6 +201,8 @@ impl<'a, I: Instance> Adc<'a, I> { .expect("Adc Init should not fail") }; + pin.mux(); + /* Reset the module. */ adc.ctrl().modify(|_, w| w.rst().held_in_reset()); adc.ctrl().modify(|_, w| w.rst().released_from_reset()); @@ -221,17 +285,20 @@ impl<'a, I: Instance> Adc<'a, I> { adc.ctrl().modify(|_, w| w.adcen().enabled()); Self { - _inst: core::marker::PhantomData, + _inst: PhantomData, } } + /// Deinitialize the ADC peripheral. pub fn deinit(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ctrl().modify(|_, w| w.adcen().disabled()); } + /// Perform offset calibration. + /// Waits for calibration to complete before returning. pub fn do_offset_calibration(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); // Enable calibration mode adc.ctrl() .modify(|_, w| w.calofs().offset_calibration_request_pending()); @@ -240,6 +307,13 @@ impl<'a, I: Instance> Adc<'a, I> { while adc.stat().read().cal_rdy().is_not_set() {} } + /// Calculate gain conversion result from gain adjustment factor. + /// + /// # Arguments + /// * `gain_adjustment` - Gain adjustment factor + /// + /// # Returns + /// Gain calibration register value pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { let mut gcra_array = [0u32; 17]; let mut gcalr: u32 = 0; @@ -258,8 +332,9 @@ impl<'a, I: Instance> Adc<'a, I> { gcalr } + /// Perform automatic gain calibration. pub fn do_auto_calibration(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} @@ -280,11 +355,21 @@ impl<'a, I: Instance> Adc<'a, I> { while adc.stat().read().cal_rdy().is_not_set() {} } + /// Trigger ADC conversion(s) via software. + /// + /// Initiates conversion(s) for the trigger(s) specified in the bitmask. + /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). + /// + /// # Arguments + /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) pub fn do_software_trigger(&self, trigger_id_mask: u32) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); } + /// Get default conversion command configuration. + /// # Returns + /// Default conversion command configuration pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { ConvCommandConfig { sample_channel_mode: Ctype::SingleEndedASideChannel, @@ -302,9 +387,17 @@ impl<'a, I: Instance> Adc<'a, I> { } } - //TBD Need to add cmdlx and cmdhx with x {2..7} + /// Set conversion command configuration. + /// + /// Configures a conversion command slot with the specified parameters. + /// Commands define how conversions are performed (channel, resolution, etc.). + /// + /// # Arguments + /// * `index` - Command index (currently only 1 is supported) + /// * `config` - Command configuration + ///TBD Need to add cmdlx and cmdhx with x {2..7} pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); match index { 1 => { @@ -338,6 +431,10 @@ impl<'a, I: Instance> Adc<'a, I> { } } + /// Get default conversion trigger configuration. + /// + /// # Returns + /// Default conversion trigger configuration pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { ConvTriggerConfig { target_command_id: Tcmd::NotValid, @@ -347,8 +444,16 @@ impl<'a, I: Instance> Adc<'a, I> { } } + /// Set conversion trigger configuration. + /// + /// Configures a trigger to initiate conversions. Triggers can be + /// activated by software or hardware signals. + /// + /// # Arguments + /// * `trigger_id` - Trigger index (0-15) + /// * `config` - Trigger configuration pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); let tctrl = &adc.tctrl(trigger_id); tctrl.write(|w| unsafe { @@ -363,47 +468,245 @@ impl<'a, I: Instance> Adc<'a, I> { }); } + /// Reset the FIFO buffer. + /// + /// Clears all pending conversion results from the FIFO. pub fn do_reset_fifo(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); } + /// Enable ADC interrupts. + /// + /// Enables the interrupt sources specified in the bitmask. + /// + /// # Arguments + /// * `mask` - Bitmask of interrupt sources to enable pub fn enable_interrupt(&self, mask: u32) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); - INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst); } - pub fn is_interrupt_triggered(&self) -> bool { - INTERRUPT_TRIGGERED.load(Ordering::Relaxed) + /// Disable ADC interrupts. + /// + /// Disables the interrupt sources specified in the bitmask. + /// + /// # Arguments + /// * `mask` - Bitmask of interrupt sources to disable + pub fn disable_interrupt(&self, mask: u32) { + let adc = &*I::ptr(); + adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); } -} -pub fn get_conv_result() -> Option { - let adc = unsafe { &*pac::Adc1::ptr() }; - let fifo = adc.resfifo0().read().bits(); - const VALID_MASK: u32 = 1 << 31; - if fifo & VALID_MASK == 0 { - return None; - } + /// Get conversion result from FIFO. + /// + /// Reads and returns the next conversion result from the FIFO. + /// Returns `None` if the FIFO is empty. + /// + /// # Returns + /// - `Some(ConvResult)` if a result is available + /// - `None` if the FIFO is empty + pub fn get_conv_result(&self) -> Option { + let adc = &*I::ptr(); + let fifo = adc.resfifo0().read().bits(); + const VALID_MASK: u32 = 1 << 31; + if fifo & VALID_MASK == 0 { + return None; + } - Some(ConvResult { - command_id_source: (fifo >> 24) & 0x0F, - loop_count_index: (fifo >> 20) & 0x0F, - trigger_id_source: (fifo >> 16) & 0x0F, - conv_value: (fifo & 0xFFFF) as u16, - }) -} + Some(ConvResult { + command_id_source: (fifo >> 24) & 0x0F, + loop_count_index: (fifo >> 20) & 0x0F, + trigger_id_source: (fifo >> 16) & 0x0F, + conv_value: (fifo & 0xFFFF) as u16, + }) + } -pub fn on_interrupt() { - if get_conv_result().is_some() { - INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst); + /// Read ADC value asynchronously. + /// + /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. + /// + /// The function: + /// 1. Enables the FIFO watermark interrupt + /// 2. Triggers a software conversion on trigger 0 + /// 3. Waits for the conversion to complete + /// 4. Returns the conversion result + /// + /// # Returns + /// 16-bit ADC conversion value + pub async fn read(&mut self) -> u16 { + let wait = WAKER.subscribe().await; + + self.enable_interrupt(0x1); + self.do_software_trigger(1); + + let _ = wait.await; + + let result = self.get_conv_result(); + result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT } } -pub struct AdcHandler; -impl crate::interrupt::typelevel::Handler for AdcHandler { +impl Handler for InterruptHandler { unsafe fn on_interrupt() { - on_interrupt(); + T::ptr().ie().modify(|r, w| w.bits(r.bits() & !0x1)); + WAKER.wake(); } } + +mod sealed { + /// Seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for I {} + +trait SealedInstance { + fn ptr() -> &'static pac::adc0::RegisterBlock; +} + + +/// ADC Instance +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + Gate { + /// Interrupt for this ADC instance. + type Interrupt: Interrupt; +} + +macro_rules! impl_instance { + ($($n:expr),*) => { + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + fn ptr() -> &'static pac::adc0::RegisterBlock { + unsafe { &*pac::[]::ptr() } + } + + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + } + } + )* + }; +} + +impl_instance!(0, 1); //ADC2 and ADC3 missing in the PAC ?? + +pub trait AdcPin: GpioPin + sealed::Sealed + PeripheralType { + /// Set the given pin to the correct muxing state + fn mux(&self); +} + +macro_rules! impl_pin { + ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { + impl $trait for crate::peripherals::$pin { + fn mux(&self) { + self.set_pull(crate::gpio::Pull::Disabled); + self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); + self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); + self.set_function(crate::pac::port0::pcr0::Mux::$func); + } + } + }; + } + +impl_pin!(P2_0, ADC0, Mux0, AdcPin); +impl_pin!(P2_4, ADC0, Mux0, AdcPin); +impl_pin!(P2_15, ADC0, Mux0, AdcPin); +impl_pin!(P2_3, ADC0, Mux0, AdcPin); +impl_pin!(P2_2, ADC0, Mux0, AdcPin); +impl_pin!(P2_12, ADC0, Mux0, AdcPin); +impl_pin!(P2_16, ADC0, Mux0, AdcPin); +impl_pin!(P2_7, ADC0, Mux0, AdcPin); +impl_pin!(P0_18, ADC0, Mux0, AdcPin); +impl_pin!(P0_19, ADC0, Mux0, AdcPin); +impl_pin!(P0_20, ADC0, Mux0, AdcPin); +impl_pin!(P0_21, ADC0, Mux0, AdcPin); +impl_pin!(P0_22, ADC0, Mux0, AdcPin); +impl_pin!(P0_23, ADC0, Mux0, AdcPin); +impl_pin!(P0_3, ADC0, Mux0, AdcPin); +impl_pin!(P0_6, ADC0, Mux0, AdcPin); +impl_pin!(P1_0, ADC0, Mux0, AdcPin); +impl_pin!(P1_1, ADC0, Mux0, AdcPin); +impl_pin!(P1_2, ADC0, Mux0, AdcPin); +impl_pin!(P1_3, ADC0, Mux0, AdcPin); +impl_pin!(P1_4, ADC0, Mux0, AdcPin); +impl_pin!(P1_5, ADC0, Mux0, AdcPin); +impl_pin!(P1_6, ADC0, Mux0, AdcPin); +impl_pin!(P1_7, ADC0, Mux0, AdcPin); +impl_pin!(P1_10, ADC0, Mux0, AdcPin); + +impl_pin!(P2_1, ADC1, Mux0, AdcPin); +impl_pin!(P2_5, ADC1, Mux0, AdcPin); +impl_pin!(P2_19, ADC1, Mux0, AdcPin); +impl_pin!(P2_6, ADC1, Mux0, AdcPin); +impl_pin!(P2_3, ADC1, Mux0, AdcPin); +impl_pin!(P2_13, ADC1, Mux0, AdcPin); +impl_pin!(P2_17, ADC1, Mux0, AdcPin); +impl_pin!(P2_7, ADC1, Mux0, AdcPin); +impl_pin!(P1_10, ADC1, Mux0, AdcPin); +impl_pin!(P1_11, ADC1, Mux0, AdcPin); +impl_pin!(P1_12, ADC1, Mux0, AdcPin); +impl_pin!(P1_13, ADC1, Mux0, AdcPin); +impl_pin!(P1_14, ADC1, Mux0, AdcPin); +impl_pin!(P1_15, ADC1, Mux0, AdcPin); +impl_pin!(P1_16, ADC1, Mux0, AdcPin); +impl_pin!(P1_17, ADC1, Mux0, AdcPin); +impl_pin!(P1_18, ADC1, Mux0, AdcPin); +impl_pin!(P1_19, ADC1, Mux0, AdcPin); +impl_pin!(P3_31, ADC1, Mux0, AdcPin); +impl_pin!(P3_30, ADC1, Mux0, AdcPin); +impl_pin!(P3_29, ADC1, Mux0, AdcPin); + +impl_pin!(P2_4, ADC2, Mux0, AdcPin); +impl_pin!(P2_10, ADC2, Mux0, AdcPin); +impl_pin!(P4_4, ADC2, Mux0, AdcPin); +impl_pin!(P2_24, ADC2, Mux0, AdcPin); +impl_pin!(P2_16, ADC2, Mux0, AdcPin); +impl_pin!(P2_12, ADC2, Mux0, AdcPin); +impl_pin!(P2_20, ADC2, Mux0, AdcPin); +impl_pin!(P2_7, ADC2, Mux0, AdcPin); +impl_pin!(P0_2, ADC2, Mux0, AdcPin); +impl_pin!(P0_4, ADC2, Mux0, AdcPin); +impl_pin!(P0_5, ADC2, Mux0, AdcPin); +impl_pin!(P0_6, ADC2, Mux0, AdcPin); +impl_pin!(P0_7, ADC2, Mux0, AdcPin); +impl_pin!(P0_12, ADC2, Mux0, AdcPin); +impl_pin!(P0_13, ADC2, Mux0, AdcPin); +impl_pin!(P0_14, ADC2, Mux0, AdcPin); +impl_pin!(P0_15, ADC2, Mux0, AdcPin); +impl_pin!(P4_0, ADC2, Mux0, AdcPin); +impl_pin!(P4_1, ADC2, Mux0, AdcPin); +impl_pin!(P4_2, ADC2, Mux0, AdcPin); +impl_pin!(P4_3, ADC2, Mux0, AdcPin); +//impl_pin!(P4_4, ADC2, Mux0, AdcPin); // Conflit with ADC2_A3 and ADC2_A20 using the same pin +impl_pin!(P4_5, ADC2, Mux0, AdcPin); +impl_pin!(P4_6, ADC2, Mux0, AdcPin); +impl_pin!(P4_7, ADC2, Mux0, AdcPin); + +impl_pin!(P2_5, ADC3, Mux0, AdcPin); +impl_pin!(P2_11, ADC3, Mux0, AdcPin); +impl_pin!(P2_23, ADC3, Mux0, AdcPin); +impl_pin!(P2_25, ADC3, Mux0, AdcPin); +impl_pin!(P2_17, ADC3, Mux0, AdcPin); +impl_pin!(P2_13, ADC3, Mux0, AdcPin); +impl_pin!(P2_21, ADC3, Mux0, AdcPin); +impl_pin!(P2_7, ADC3, Mux0, AdcPin); +impl_pin!(P3_2, ADC3, Mux0, AdcPin); +impl_pin!(P3_3, ADC3, Mux0, AdcPin); +impl_pin!(P3_4, ADC3, Mux0, AdcPin); +impl_pin!(P3_5, ADC3, Mux0, AdcPin); +impl_pin!(P3_6, ADC3, Mux0, AdcPin); +impl_pin!(P3_7, ADC3, Mux0, AdcPin); +impl_pin!(P3_12, ADC3, Mux0, AdcPin); +impl_pin!(P3_13, ADC3, Mux0, AdcPin); +impl_pin!(P3_14, ADC3, Mux0, AdcPin); +impl_pin!(P3_15, ADC3, Mux0, AdcPin); +impl_pin!(P3_20, ADC3, Mux0, AdcPin); +impl_pin!(P3_21, ADC3, Mux0, AdcPin); +impl_pin!(P3_22, ADC3, Mux0, AdcPin); +impl_pin!(P3_23, ADC3, Mux0, AdcPin); +impl_pin!(P3_24, ADC3, Mux0, AdcPin); +impl_pin!(P3_25, ADC3, Mux0, AdcPin); diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 014a12519..667d79536 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs @@ -945,7 +945,10 @@ pub(crate) mod gate { impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); + impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig); impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); + impl_cc_gate!(ADC2, mrcc_glb_cc1, mrcc_glb_rst1, adc2, AdcConfig); + impl_cc_gate!(ADC3, mrcc_glb_cc1, mrcc_glb_rst1, adc3, AdcConfig); // DMA0 peripheral - uses NoConfig since it has no selectable clock source impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig); diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs index fed5e558e..f2f51c60c 100644 --- a/embassy-mcxa/src/clocks/periph_helpers.rs +++ b/embassy-mcxa/src/clocks/periph_helpers.rs @@ -427,6 +427,7 @@ impl SPConfHelper for OsTimerConfig { /// Selectable clocks for the ADC peripheral #[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AdcClockSel { /// Divided `fro_lf`/`clk_12m`/FRO12M source FroLfDiv, diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs index c960af7a2..7eabc6495 100644 --- a/embassy-mcxa/src/interrupt.rs +++ b/embassy-mcxa/src/interrupt.rs @@ -9,7 +9,10 @@ mod generated { #[rustfmt::skip] embassy_hal_internal::interrupt_mod!( + ADC0, ADC1, + ADC2, + ADC3, DMA_CH0, DMA_CH1, DMA_CH2, @@ -279,11 +282,48 @@ impl InterruptExt for Rtc { cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC) } } +pub struct Adc0; +pub const ADC0: Adc0 = Adc0; -pub struct Adc; -pub const ADC1: Adc = Adc; +impl InterruptExt for Adc0 { + /// Clear any pending ADC0 in NVIC. + #[inline] + fn unpend(&self) { + cortex_m::peripheral::NVIC::unpend(Interrupt::ADC0); + } + + /// Set NVIC priority for ADC0. + #[inline] + fn set_priority(&self, priority: Priority) { + unsafe { + let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; + nvic.set_priority(Interrupt::ADC0, u8::from(priority)); + } + } -impl InterruptExt for Adc { + /// Enable ADC0 in NVIC. + #[inline] + unsafe fn enable(&self) { + cortex_m::peripheral::NVIC::unmask(Interrupt::ADC0); + } + + /// Disable ADC0 in NVIC. + #[inline] + unsafe fn disable(&self) { + cortex_m::peripheral::NVIC::mask(Interrupt::ADC0); + } + + /// Check if ADC0 is pending in NVIC. + #[inline] + fn is_pending(&self) -> bool { + cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC0) + } +} + +pub struct Adc1; +pub const ADC1: Adc1 = Adc1; + +impl InterruptExt for Adc1 { /// Clear any pending ADC1 in NVIC. #[inline] fn unpend(&self) { @@ -318,6 +358,82 @@ impl InterruptExt for Adc { } } +pub struct Adc2; +pub const ADC2: Adc2 = Adc2; + +impl InterruptExt for Adc2 { + /// Clear any pending ADC2 in NVIC. + #[inline] + fn unpend(&self) { + cortex_m::peripheral::NVIC::unpend(Interrupt::ADC2); + } + + /// Set NVIC priority for ADC2. + #[inline] + fn set_priority(&self, priority: Priority) { + unsafe { + let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; + nvic.set_priority(Interrupt::ADC2, u8::from(priority)); + } + } + + /// Enable ADC2 in NVIC. + #[inline] + unsafe fn enable(&self) { + cortex_m::peripheral::NVIC::unmask(Interrupt::ADC2); + } + + /// Disable ADC2 in NVIC. + #[inline] + unsafe fn disable(&self) { + cortex_m::peripheral::NVIC::mask(Interrupt::ADC2); + } + + /// Check if ADC2 is pending in NVIC. + #[inline] + fn is_pending(&self) -> bool { + cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC2) + } +} + +pub struct Adc3; +pub const ADC3: Adc3 = Adc3; + +impl InterruptExt for Adc3 { + /// Clear any pending ADC3 in NVIC. + #[inline] + fn unpend(&self) { + cortex_m::peripheral::NVIC::unpend(Interrupt::ADC3); + } + + /// Set NVIC priority for ADC3. + #[inline] + fn set_priority(&self, priority: Priority) { + unsafe { + let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; + nvic.set_priority(Interrupt::ADC3, u8::from(priority)); + } + } + + /// Enable ADC3 in NVIC. + #[inline] + unsafe fn enable(&self) { + cortex_m::peripheral::NVIC::unmask(Interrupt::ADC3); + } + + /// Disable ADC3 in NVIC. + #[inline] + unsafe fn disable(&self) { + cortex_m::peripheral::NVIC::mask(Interrupt::ADC3); + } + + /// Check if ADC3 is pending in NVIC. + #[inline] + fn is_pending(&self) -> bool { + cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC3) + } +} + pub struct Gpio0; pub const GPIO0: Gpio0 = Gpio0; diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index 1bbdffa06..10b6167b6 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -8,7 +8,6 @@ pub mod clocks; // still provide clock helpers pub mod dma; pub mod gpio; -pub mod pins; // pin mux helpers pub mod adc; pub mod clkout; @@ -26,6 +25,8 @@ pub use crate::pac::NVIC_PRIO_BITS; embassy_hal_internal::peripherals!( ADC0, ADC1, + ADC2, + ADC3, AOI0, AOI1, @@ -336,7 +337,6 @@ embassy_hal_internal::peripherals!( // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. // Re-export interrupt traits and types -pub use adc::Adc1 as Adc1Token; pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; pub use interrupt::InterruptExt; #[cfg(feature = "unstable-pac")] @@ -354,8 +354,14 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps + crate::interrupt::ADC0.set_priority(cfg.adc_interrupt_priority); + // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps + crate::interrupt::ADC2.set_priority(cfg.adc_interrupt_priority); + // Apply user-configured priority early; enabling is left to examples/apps + crate::interrupt::ADC3.set_priority(cfg.adc_interrupt_priority); + // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs deleted file mode 100644 index 9adbe64c8..000000000 --- a/embassy-mcxa/src/pins.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Pin configuration helpers (separate from peripheral drivers). -use crate::pac; - -/// Configure pins for ADC usage. -/// -/// # Safety -/// -/// Must be called after PORT clocks are enabled. -pub unsafe fn configure_adc_pins() { - // P1_10 = ADC1_A8 - let port1 = &*pac::Port1::ptr(); - port1.pcr10().write(|w| { - w.ps() - .ps0() - .pe() - .pe0() - .sre() - .sre0() - .ode() - .ode0() - .dse() - .dse0() - .mux() - .mux0() - .ibe() - .ibe0() - .inv() - .inv0() - .lk() - .lk0() - }); - core::arch::asm!("dsb sy; isb sy"); -} diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index c88b1fe8d..9db1173e3 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -2,35 +2,33 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa_examples::init_adc_pins; -use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; +use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy, InterruptHandler}; use hal::clocks::PoweredClock; +use hal::config::Config; +use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; use hal::pac::adc1::tctrl::Tcmd; -use hal::{InterruptExt, bind_interrupts}; +use hal::bind_interrupts; +use hal::peripherals::ADC1; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; bind_interrupts!(struct Irqs { - ADC1 => hal::adc::AdcHandler; + ADC1 => InterruptHandler; }); -#[used] -#[no_mangle] -static KEEP_ADC: unsafe extern "C" fn() = ADC1; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); defmt::info!("ADC interrupt Example"); - unsafe { - init_adc_pins(); - } - let adc_config = LpadcConfig { enable_in_doze_mode: true, conversion_average_mode: CalAvgs::Average128, @@ -46,7 +44,7 @@ async fn main(_spawner: Spawner) { source: AdcClockSel::FroLfDiv, div: Div4::no_div(), }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + let mut adc = Adc::new(p.ADC1, p.P1_10, Irqs, adc_config); adc.do_offset_calibration(); adc.do_auto_calibration(); @@ -63,22 +61,8 @@ async fn main(_spawner: Spawner) { defmt::info!("ADC configuration done..."); - adc.enable_interrupt(0x1); - - unsafe { - hal::interrupt::ADC1.enable(); - } - - unsafe { - cortex_m::interrupt::enable(); - } - loop { - adc.do_software_trigger(1); - while !adc.is_interrupt_triggered() { - // Wait until the interrupt is triggered - } - defmt::info!("*** ADC interrupt TRIGGERED! ***"); - //TBD need to print the value + let value = adc.read().await; + defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); } } diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 07c50f224..65b66c8dd 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -2,9 +2,10 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa_examples::init_adc_pins; -use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; +use hal::adc::{Adc, ConvResult, LpadcConfig, TriggerPriorityPolicy}; use hal::clocks::PoweredClock; +use hal::config::Config; +use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; @@ -16,11 +17,10 @@ const G_LPADC_RESULT_SHIFT: u32 = 0; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - unsafe { - init_adc_pins(); - } + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); defmt::info!("=== ADC polling Example ==="); @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { source: AdcClockSel::FroLfDiv, div: Div4::no_div(), }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + let adc = Adc::new_polling(p.ADC1, p.P1_10, adc_config); adc.do_offset_calibration(); adc.do_auto_calibration(); @@ -60,7 +60,7 @@ async fn main(_spawner: Spawner) { adc.do_software_trigger(1); let mut result: Option = None; while result.is_none() { - result = hal::adc::get_conv_result(); + result = adc.get_conv_result(); } let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; defmt::info!("value: {=u16}", value); diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs deleted file mode 100644 index 2573a6adc..000000000 --- a/examples/mcxa/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_std] -#![allow(clippy::missing_safety_doc)] - -//! Shared board-specific helpers for the FRDM-MCXA276 examples. -//! These live with the examples so the HAL stays generic. - -use hal::{clocks, pins}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -/// Initialize clocks and pin muxing for ADC. -pub unsafe fn init_adc_pins() { - // NOTE: Lpuart has been updated to properly enable + reset its own clocks. - // GPIO has not. - _ = clocks::enable_and_reset::(&clocks::periph_helpers::NoConfig); - pins::configure_adc_pins(); -} -- cgit From f20e4225ddd03f89dc48835355eeff5c2143038a Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 10:01:20 -0800 Subject: Run cargo fmt Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 58 ++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 2e2fb0342..1e3a6952a 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -3,11 +3,11 @@ use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; -use paste::paste; -use crate::pac; -use crate::interrupt::typelevel::{Handler, Interrupt}; use crate::gpio::{GpioPin, SealedPin}; +use crate::interrupt::typelevel::{Handler, Interrupt}; +use crate::pac; use maitake_sync::WaitCell; +use paste::paste; use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; use crate::clocks::{Gate, PoweredClock, enable_and_reset}; @@ -42,8 +42,8 @@ pub enum TriggerPriorityPolicy { /// Configuration for the LPADC peripheral. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LpadcConfig { - /// Control system transition to Stop and Wait power modes while ADC is converting. - /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. + /// Control system transition to Stop and Wait power modes while ADC is converting. + /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. pub enable_in_doze_mode: bool, /// Auto-Calibration Averages. @@ -58,14 +58,14 @@ pub struct LpadcConfig { pub power_level_mode: Pwrsel, /// Trigger priority policy for handling multiple triggers pub trigger_priority_policy: TriggerPriorityPolicy, - /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, + /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. pub enable_conv_pause: bool, - /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. + /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. pub conv_pause_delay: u16, - /// FIFO watermark level for interrupt generation. - /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field, + /// FIFO watermark level for interrupt generation. + /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field, /// the ready flag would be asserted to indicate stored data has reached the programmable threshold. pub fifo_watermark: u8, /// Power configuration (normal/deep sleep behavior) @@ -115,7 +115,6 @@ pub struct ConvCommandConfig { pub enable_wait_trigger: bool, } - /// Configuration for a conversion trigger. /// /// Defines how a trigger initiates ADC conversions. @@ -166,7 +165,7 @@ impl<'a, I: Instance> Adc<'a, I> { I::Interrupt::unpend(); unsafe { I::Interrupt::enable() }; - + adc } @@ -176,20 +175,12 @@ impl<'a, I: Instance> Adc<'a, I> { /// * `_inst` - ADC peripheral instance /// * `pin` - GPIO pin to use for ADC input /// * `config` - ADC configuration - pub fn new_polling( - _inst: Peri<'a, I>, - pin: Peri<'a, impl AdcPin>, - config: LpadcConfig, - ) -> Self { + pub fn new_polling(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Self { Self::new_inner(_inst, pin, config) } /// Internal initialization function shared by `new` and `new_polling`. - fn new_inner( - _inst: Peri<'a, I>, - pin: Peri<'a, impl AdcPin>, - config: LpadcConfig, - ) -> Self { + fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Self { let adc = &*I::ptr(); let _clock_freq = unsafe { @@ -284,9 +275,7 @@ impl<'a, I: Instance> Adc<'a, I> { // Enable ADC adc.ctrl().modify(|_, w| w.adcen().enabled()); - Self { - _inst: PhantomData, - } + Self { _inst: PhantomData } } /// Deinitialize the ADC peripheral. @@ -565,7 +554,6 @@ trait SealedInstance { fn ptr() -> &'static pac::adc0::RegisterBlock; } - /// ADC Instance #[allow(private_bounds)] pub trait Instance: SealedInstance + PeripheralType + Gate { @@ -600,17 +588,17 @@ pub trait AdcPin: GpioPin + sealed::Sealed + PeripheralType { } macro_rules! impl_pin { - ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { - impl $trait for crate::peripherals::$pin { - fn mux(&self) { - self.set_pull(crate::gpio::Pull::Disabled); - self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); - self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); - self.set_function(crate::pac::port0::pcr0::Mux::$func); - } + ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { + impl $trait for crate::peripherals::$pin { + fn mux(&self) { + self.set_pull(crate::gpio::Pull::Disabled); + self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); + self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); + self.set_function(crate::pac::port0::pcr0::Mux::$func); } - }; - } + } + }; +} impl_pin!(P2_0, ADC0, Mux0, AdcPin); impl_pin!(P2_4, ADC0, Mux0, AdcPin); -- cgit From b9bb7c0ebdbe19832ed9e9d75b12df86523c5fd2 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 11:12:33 -0800 Subject: Modify set_conv_command_config to support command index from 1 to 7 Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 94 ++++++++++++++++++---------------- examples/mcxa/src/bin/adc_interrupt.rs | 4 +- examples/mcxa/src/bin/adc_polling.rs | 4 +- 3 files changed, 55 insertions(+), 47 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 1e3a6952a..c2ea06e89 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -101,17 +101,17 @@ impl Default for LpadcConfig { /// Defines the parameters for a single ADC conversion operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvCommandConfig { - pub sample_channel_mode: Ctype, - pub channel_number: Adch, - pub chained_next_command_number: Next, + pub sample_channel_mode: u8, + pub channel_number: u8, + pub chained_next_command_number: u8, pub enable_auto_channel_increment: bool, pub loop_count: u8, - pub hardware_average_mode: Avgs, - pub sample_time_mode: Sts, - pub hardware_compare_mode: Cmpen, + pub hardware_average_mode: u8, + pub sample_time_mode: u8, + pub hardware_compare_mode: u8, pub hardware_compare_value_high: u32, pub hardware_compare_value_low: u32, - pub conversion_resolution_mode: Mode, + pub conversion_resolution_mode: u8, pub enable_wait_trigger: bool, } @@ -361,17 +361,17 @@ impl<'a, I: Instance> Adc<'a, I> { /// Default conversion command configuration pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { ConvCommandConfig { - sample_channel_mode: Ctype::SingleEndedASideChannel, - channel_number: Adch::SelectCh0, - chained_next_command_number: Next::NoNextCmdTerminateOnFinish, + sample_channel_mode: Ctype::SingleEndedASideChannel as u8, + channel_number: Adch::SelectCh0 as u8, + chained_next_command_number: Next::NoNextCmdTerminateOnFinish as u8, enable_auto_channel_increment: false, loop_count: 0, - hardware_average_mode: Avgs::NoAverage, - sample_time_mode: Sts::Sample3p5, - hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, + hardware_average_mode: Avgs::NoAverage as u8, + sample_time_mode: Sts::Sample3p5 as u8, + hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult as u8, hardware_compare_value_high: 0, hardware_compare_value_low: 0, - conversion_resolution_mode: Mode::Data12Bits, + conversion_resolution_mode: Mode::Data12Bits as u8, enable_wait_trigger: false, } } @@ -382,40 +382,48 @@ impl<'a, I: Instance> Adc<'a, I> { /// Commands define how conversions are performed (channel, resolution, etc.). /// /// # Arguments - /// * `index` - Command index (currently only 1 is supported) + /// * `index` - Command index /// * `config` - Command configuration - ///TBD Need to add cmdlx and cmdhx with x {2..7} pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { let adc = &*I::ptr(); + macro_rules! write_cmd { + ($idx:expr) => {{ + paste! { + adc.[]().write(|w| unsafe { + w.adch() + .bits(config.channel_number) + .mode() + .bit(config.conversion_resolution_mode != 0) + }); + adc.[]().write(|w| unsafe { + w.next() + .bits(config.chained_next_command_number) + .loop_() + .bits(config.loop_count) + .avgs() + .bits(config.hardware_average_mode) + .sts() + .bits(config.sample_time_mode) + .cmpen() + .bits(config.hardware_compare_mode) + .wait_trig() + .bit(config.enable_wait_trigger) + .lwi() + .bit(config.enable_auto_channel_increment) + }); + } + }}; + } + match index { - 1 => { - adc.cmdl1().write(|w| { - w.adch() - .variant(config.channel_number) - .mode() - .variant(config.conversion_resolution_mode) - }); - adc.cmdh1().write(|w| unsafe { - w.next() - .variant(config.chained_next_command_number) - .loop_() - .bits(config.loop_count) - .avgs() - .variant(config.hardware_average_mode) - .sts() - .variant(config.sample_time_mode) - .cmpen() - .variant(config.hardware_compare_mode); - if config.enable_wait_trigger { - w.wait_trig().enabled(); - } - if config.enable_auto_channel_increment { - w.lwi().enabled(); - } - w - }); - } + 1 => write_cmd!(1), + 2 => write_cmd!(2), + 3 => write_cmd!(3), + 4 => write_cmd!(4), + 5 => write_cmd!(5), + 6 => write_cmd!(6), + 7 => write_cmd!(7), _ => panic!("Invalid command index: must be between 1 and 7"), } } diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 9db1173e3..125e37690 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -50,8 +50,8 @@ async fn main(_spawner: Spawner) { adc.do_auto_calibration(); let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8 as u8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits as u8; adc.set_conv_command_config(1, &conv_command_config); let mut conv_trigger_config = adc.get_default_conv_trigger_config(); diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 65b66c8dd..523035e0f 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -45,8 +45,8 @@ async fn main(_spawner: Spawner) { adc.do_auto_calibration(); let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8 as u8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits as u8; adc.set_conv_command_config(1, &conv_command_config); let mut conv_trigger_config = adc.get_default_conv_trigger_config(); -- cgit From 0da28a271816b319c142254cb3598fd14096813a Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 13:29:16 -0800 Subject: Remove duplication of interrupt trait and use embassy_hal_internal Signed-off-by: Mathis Deroo --- embassy-mcxa/src/interrupt.rs | 151 ------------------------------------------ embassy-mcxa/src/lib.rs | 8 --- 2 files changed, 159 deletions(-) diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs index 7eabc6495..725f8d499 100644 --- a/embassy-mcxa/src/interrupt.rs +++ b/embassy-mcxa/src/interrupt.rs @@ -282,157 +282,6 @@ impl InterruptExt for Rtc { cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC) } } -pub struct Adc0; -pub const ADC0: Adc0 = Adc0; - -impl InterruptExt for Adc0 { - /// Clear any pending ADC0 in NVIC. - #[inline] - fn unpend(&self) { - cortex_m::peripheral::NVIC::unpend(Interrupt::ADC0); - } - - /// Set NVIC priority for ADC0. - #[inline] - fn set_priority(&self, priority: Priority) { - unsafe { - let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; - nvic.set_priority(Interrupt::ADC0, u8::from(priority)); - } - } - - /// Enable ADC0 in NVIC. - #[inline] - unsafe fn enable(&self) { - cortex_m::peripheral::NVIC::unmask(Interrupt::ADC0); - } - - /// Disable ADC0 in NVIC. - #[inline] - unsafe fn disable(&self) { - cortex_m::peripheral::NVIC::mask(Interrupt::ADC0); - } - - /// Check if ADC0 is pending in NVIC. - #[inline] - fn is_pending(&self) -> bool { - cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC0) - } -} - -pub struct Adc1; -pub const ADC1: Adc1 = Adc1; - -impl InterruptExt for Adc1 { - /// Clear any pending ADC1 in NVIC. - #[inline] - fn unpend(&self) { - cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1); - } - - /// Set NVIC priority for ADC1. - #[inline] - fn set_priority(&self, priority: Priority) { - unsafe { - let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; - nvic.set_priority(Interrupt::ADC1, u8::from(priority)); - } - } - - /// Enable ADC1 in NVIC. - #[inline] - unsafe fn enable(&self) { - cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1); - } - - /// Disable ADC1 in NVIC. - #[inline] - unsafe fn disable(&self) { - cortex_m::peripheral::NVIC::mask(Interrupt::ADC1); - } - - /// Check if ADC1 is pending in NVIC. - #[inline] - fn is_pending(&self) -> bool { - cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1) - } -} - -pub struct Adc2; -pub const ADC2: Adc2 = Adc2; - -impl InterruptExt for Adc2 { - /// Clear any pending ADC2 in NVIC. - #[inline] - fn unpend(&self) { - cortex_m::peripheral::NVIC::unpend(Interrupt::ADC2); - } - - /// Set NVIC priority for ADC2. - #[inline] - fn set_priority(&self, priority: Priority) { - unsafe { - let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; - nvic.set_priority(Interrupt::ADC2, u8::from(priority)); - } - } - - /// Enable ADC2 in NVIC. - #[inline] - unsafe fn enable(&self) { - cortex_m::peripheral::NVIC::unmask(Interrupt::ADC2); - } - - /// Disable ADC2 in NVIC. - #[inline] - unsafe fn disable(&self) { - cortex_m::peripheral::NVIC::mask(Interrupt::ADC2); - } - - /// Check if ADC2 is pending in NVIC. - #[inline] - fn is_pending(&self) -> bool { - cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC2) - } -} - -pub struct Adc3; -pub const ADC3: Adc3 = Adc3; - -impl InterruptExt for Adc3 { - /// Clear any pending ADC3 in NVIC. - #[inline] - fn unpend(&self) { - cortex_m::peripheral::NVIC::unpend(Interrupt::ADC3); - } - - /// Set NVIC priority for ADC3. - #[inline] - fn set_priority(&self, priority: Priority) { - unsafe { - let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; - nvic.set_priority(Interrupt::ADC3, u8::from(priority)); - } - } - - /// Enable ADC3 in NVIC. - #[inline] - unsafe fn enable(&self) { - cortex_m::peripheral::NVIC::unmask(Interrupt::ADC3); - } - - /// Disable ADC3 in NVIC. - #[inline] - unsafe fn disable(&self) { - cortex_m::peripheral::NVIC::mask(Interrupt::ADC3); - } - - /// Check if ADC3 is pending in NVIC. - #[inline] - fn is_pending(&self) -> bool { - cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC3) - } -} pub struct Gpio0; pub const GPIO0: Gpio0 = Gpio0; diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index 10b6167b6..be279e509 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -354,14 +354,6 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps - crate::interrupt::ADC0.set_priority(cfg.adc_interrupt_priority); - // Apply user-configured priority early; enabling is left to examples/apps - crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); - // Apply user-configured priority early; enabling is left to examples/apps - crate::interrupt::ADC2.set_priority(cfg.adc_interrupt_priority); - // Apply user-configured priority early; enabling is left to examples/apps - crate::interrupt::ADC3.set_priority(cfg.adc_interrupt_priority); - // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); -- cgit From e39040afc7ee4080a1d3559d0b7d578420301726 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 13:40:59 -0800 Subject: Implement WaitCell per ADC instance Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index c2ea06e89..94d945c88 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -18,8 +18,6 @@ use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; use crate::pac::adc1::ctrl::CalAvgs; use crate::pac::adc1::tctrl::{Tcmd, Tpri}; -/// Global wait cell for alarm notifications -static WAKER: WaitCell = WaitCell::new(); const G_LPADC_RESULT_SHIFT: u32 = 0; @@ -532,7 +530,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// # Returns /// 16-bit ADC conversion value pub async fn read(&mut self) -> u16 { - let wait = WAKER.subscribe().await; + let wait = I::wait_cell().subscribe().await; self.enable_interrupt(0x1); self.do_software_trigger(1); @@ -547,7 +545,7 @@ impl<'a, I: Instance> Adc<'a, I> { impl Handler for InterruptHandler { unsafe fn on_interrupt() { T::ptr().ie().modify(|r, w| w.bits(r.bits() & !0x1)); - WAKER.wake(); + T::wait_cell().wake(); } } @@ -560,6 +558,7 @@ impl sealed::Sealed for I {} trait SealedInstance { fn ptr() -> &'static pac::adc0::RegisterBlock; + fn wait_cell() -> &'static WaitCell; } /// ADC Instance @@ -578,6 +577,11 @@ macro_rules! impl_instance { unsafe { &*pac::[]::ptr() } } + fn wait_cell() -> &'static WaitCell { + static WAIT_CELL: WaitCell = WaitCell::new(); + &WAIT_CELL + } + } impl Instance for crate::peripherals::[] { -- cgit From ff068148fcbc7ea148454cffa81921cc0e0da3e9 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 13:56:31 -0800 Subject: Modify ConvCommandConfig struct Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 38 ++++++++++++++++------------------ examples/mcxa/src/bin/adc_interrupt.rs | 4 ++-- examples/mcxa/src/bin/adc_polling.rs | 4 ++-- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 94d945c88..8852fd737 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -99,17 +99,16 @@ impl Default for LpadcConfig { /// Defines the parameters for a single ADC conversion operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvCommandConfig { - pub sample_channel_mode: u8, - pub channel_number: u8, - pub chained_next_command_number: u8, + pub channel_number: Adch, + pub chained_next_command_number: Next, pub enable_auto_channel_increment: bool, pub loop_count: u8, - pub hardware_average_mode: u8, - pub sample_time_mode: u8, - pub hardware_compare_mode: u8, + pub hardware_average_mode: Avgs, + pub sample_time_mode: Sts, + pub hardware_compare_mode: Cmpen, pub hardware_compare_value_high: u32, pub hardware_compare_value_low: u32, - pub conversion_resolution_mode: u8, + pub conversion_resolution_mode: Mode, pub enable_wait_trigger: bool, } @@ -359,17 +358,16 @@ impl<'a, I: Instance> Adc<'a, I> { /// Default conversion command configuration pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { ConvCommandConfig { - sample_channel_mode: Ctype::SingleEndedASideChannel as u8, - channel_number: Adch::SelectCh0 as u8, - chained_next_command_number: Next::NoNextCmdTerminateOnFinish as u8, + channel_number: Adch::SelectCh0, + chained_next_command_number: Next::NoNextCmdTerminateOnFinish, enable_auto_channel_increment: false, loop_count: 0, - hardware_average_mode: Avgs::NoAverage as u8, - sample_time_mode: Sts::Sample3p5 as u8, - hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult as u8, + hardware_average_mode: Avgs::NoAverage, + sample_time_mode: Sts::Sample3p5, + hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, hardware_compare_value_high: 0, hardware_compare_value_low: 0, - conversion_resolution_mode: Mode::Data12Bits as u8, + conversion_resolution_mode: Mode::Data12Bits, enable_wait_trigger: false, } } @@ -390,21 +388,21 @@ impl<'a, I: Instance> Adc<'a, I> { paste! { adc.[]().write(|w| unsafe { w.adch() - .bits(config.channel_number) + .bits(config.channel_number.into()) .mode() - .bit(config.conversion_resolution_mode != 0) + .bit(config.conversion_resolution_mode.into()) }); adc.[]().write(|w| unsafe { w.next() - .bits(config.chained_next_command_number) + .bits(config.chained_next_command_number.into()) .loop_() .bits(config.loop_count) .avgs() - .bits(config.hardware_average_mode) + .bits(config.hardware_average_mode.into()) .sts() - .bits(config.sample_time_mode) + .bits(config.sample_time_mode.into()) .cmpen() - .bits(config.hardware_compare_mode) + .bits(config.hardware_compare_mode.into()) .wait_trig() .bit(config.enable_wait_trigger) .lwi() diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 125e37690..9db1173e3 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -50,8 +50,8 @@ async fn main(_spawner: Spawner) { adc.do_auto_calibration(); let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8 as u8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits as u8; + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; adc.set_conv_command_config(1, &conv_command_config); let mut conv_trigger_config = adc.get_default_conv_trigger_config(); diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 523035e0f..65b66c8dd 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -45,8 +45,8 @@ async fn main(_spawner: Spawner) { adc.do_auto_calibration(); let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8 as u8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits as u8; + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; adc.set_conv_command_config(1, &conv_command_config); let mut conv_trigger_config = adc.get_default_conv_trigger_config(); -- cgit From 93b0f0308abee5607efae799039e0f4ccf2914b6 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 14:08:34 -0800 Subject: Use Result enum for ConvResult structure and read function Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 24 +++++++++++++++++------- examples/mcxa/src/bin/adc_interrupt.rs | 10 ++++++++-- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 8852fd737..81b0b02f8 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -123,6 +123,16 @@ pub struct ConvTriggerConfig { pub enable_hardware_trigger: bool, } +/// ADC Error types +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AdcError { + /// FIFO is empty, no conversion result available + FifoEmpty, + /// Invalid configuration + InvalidConfig, +} + /// Result of an ADC conversion. /// /// Contains the conversion value and metadata about the conversion. @@ -498,16 +508,16 @@ impl<'a, I: Instance> Adc<'a, I> { /// /// # Returns /// - `Some(ConvResult)` if a result is available - /// - `None` if the FIFO is empty - pub fn get_conv_result(&self) -> Option { + /// - `Err(AdcError::FifoEmpty)` if the FIFO is empty + pub fn get_conv_result(&self) -> Result { let adc = &*I::ptr(); let fifo = adc.resfifo0().read().bits(); const VALID_MASK: u32 = 1 << 31; if fifo & VALID_MASK == 0 { - return None; + return Err(AdcError::FifoEmpty); } - Some(ConvResult { + Ok(ConvResult { command_id_source: (fifo >> 24) & 0x0F, loop_count_index: (fifo >> 20) & 0x0F, trigger_id_source: (fifo >> 16) & 0x0F, @@ -527,7 +537,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// /// # Returns /// 16-bit ADC conversion value - pub async fn read(&mut self) -> u16 { + pub async fn read(&mut self) -> Result { let wait = I::wait_cell().subscribe().await; self.enable_interrupt(0x1); @@ -535,8 +545,8 @@ impl<'a, I: Instance> Adc<'a, I> { let _ = wait.await; - let result = self.get_conv_result(); - result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT + let result = self.get_conv_result().unwrap().conv_value >> G_LPADC_RESULT_SHIFT; + Ok(result) } } diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 9db1173e3..a0e392634 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -62,7 +62,13 @@ async fn main(_spawner: Spawner) { defmt::info!("ADC configuration done..."); loop { - let value = adc.read().await; - defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); + match adc.read().await { + Ok(value) => { + defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); + } + Err(e) => { + defmt::error!("ADC read error: {:?}", e); + } + } } } -- cgit From e75e8dddb63f758419ef5d8d7c321c631f5b0a59 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 14:12:49 -0800 Subject: Remove dereference for Instance ptr Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 81b0b02f8..f7340e8c2 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -188,7 +188,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// Internal initialization function shared by `new` and `new_polling`. fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Self { - let adc = &*I::ptr(); + let adc = I::ptr(); let _clock_freq = unsafe { enable_and_reset::(&AdcConfig { @@ -287,14 +287,14 @@ impl<'a, I: Instance> Adc<'a, I> { /// Deinitialize the ADC peripheral. pub fn deinit(&self) { - let adc = &*I::ptr(); + let adc = I::ptr(); adc.ctrl().modify(|_, w| w.adcen().disabled()); } /// Perform offset calibration. /// Waits for calibration to complete before returning. pub fn do_offset_calibration(&self) { - let adc = &*I::ptr(); + let adc = I::ptr(); // Enable calibration mode adc.ctrl() .modify(|_, w| w.calofs().offset_calibration_request_pending()); @@ -330,7 +330,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// Perform automatic gain calibration. pub fn do_auto_calibration(&self) { - let adc = &*I::ptr(); + let adc = I::ptr(); adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} @@ -359,7 +359,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// # Arguments /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) pub fn do_software_trigger(&self, trigger_id_mask: u32) { - let adc = &*I::ptr(); + let adc = I::ptr(); adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); } @@ -391,7 +391,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// * `index` - Command index /// * `config` - Command configuration pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { - let adc = &*I::ptr(); + let adc = I::ptr(); macro_rules! write_cmd { ($idx:expr) => {{ @@ -456,7 +456,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// * `trigger_id` - Trigger index (0-15) /// * `config` - Trigger configuration pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { - let adc = &*I::ptr(); + let adc = I::ptr(); let tctrl = &adc.tctrl(trigger_id); tctrl.write(|w| unsafe { @@ -475,7 +475,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// /// Clears all pending conversion results from the FIFO. pub fn do_reset_fifo(&self) { - let adc = &*I::ptr(); + let adc = I::ptr(); adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); } @@ -486,7 +486,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// # Arguments /// * `mask` - Bitmask of interrupt sources to enable pub fn enable_interrupt(&self, mask: u32) { - let adc = &*I::ptr(); + let adc = I::ptr(); adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); } @@ -497,7 +497,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// # Arguments /// * `mask` - Bitmask of interrupt sources to disable pub fn disable_interrupt(&self, mask: u32) { - let adc = &*I::ptr(); + let adc = I::ptr(); adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); } @@ -510,7 +510,7 @@ impl<'a, I: Instance> Adc<'a, I> { /// - `Some(ConvResult)` if a result is available /// - `Err(AdcError::FifoEmpty)` if the FIFO is empty pub fn get_conv_result(&self) -> Result { - let adc = &*I::ptr(); + let adc = I::ptr(); let fifo = adc.resfifo0().read().bits(); const VALID_MASK: u32 = 1 << 31; if fifo & VALID_MASK == 0 { -- cgit From 759ab109806f447a1193954a453828b860104c3a Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 15:09:05 -0800 Subject: Add Mode trait for Async and Blocking behavior Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 131 +++++++++++++++++++++------------ examples/mcxa/src/bin/adc_interrupt.rs | 2 +- examples/mcxa/src/bin/adc_polling.rs | 20 +++-- 3 files changed, 96 insertions(+), 57 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index f7340e8c2..16b40fa45 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -10,11 +10,11 @@ use maitake_sync::WaitCell; use paste::paste; use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; -use crate::clocks::{Gate, PoweredClock, enable_and_reset}; +use crate::clocks::{Gate, PoweredClock, enable_and_reset, ClockError}; use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; -use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; +use crate::pac::adc1::cmdl1::{Adch, Mode}; use crate::pac::adc1::ctrl::CalAvgs; use crate::pac::adc1::tctrl::{Tcmd, Tpri}; @@ -123,14 +123,19 @@ pub struct ConvTriggerConfig { pub enable_hardware_trigger: bool, } +/// Shorthand for `Result`. +pub type Result = core::result::Result; + /// ADC Error types #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AdcError { +pub enum Error { /// FIFO is empty, no conversion result available FifoEmpty, /// Invalid configuration InvalidConfig, + /// Clock configuration error. + ClockSetup(ClockError), } /// Result of an ADC conversion. @@ -150,11 +155,28 @@ pub struct InterruptHandler { } /// ADC driver instance. -pub struct Adc<'a, I: Instance> { +pub struct Adc<'a, I: Instance, M: ModeAdc> { _inst: PhantomData<&'a mut I>, + _phantom: PhantomData, } -impl<'a, I: Instance> Adc<'a, I> { +impl<'a, I: Instance> Adc<'a, I, Blocking> { + /// Create a new blocking instance of the ADC driver. + /// # Arguments + /// * `_inst` - ADC peripheral instance + /// * `pin` - GPIO pin to use for ADC + /// * `config` - ADC configuration + pub fn new_blocking( + _inst: Peri<'a, I>, + pin: Peri<'a, + impl AdcPin>, + config: LpadcConfig + ) -> Result { + Self::new_inner(_inst, pin, config) + } +} + +impl<'a, I: Instance> Adc<'a, I, Async> { /// Initialize ADC with interrupt support. /// /// # Arguments @@ -162,12 +184,12 @@ impl<'a, I: Instance> Adc<'a, I> { /// * `pin` - GPIO pin to use for ADC /// * `_irq` - Interrupt binding for this ADC instance /// * `config` - ADC configuration - pub fn new( + pub fn new_async( _inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, _irq: impl crate::interrupt::typelevel::Binding> + 'a, config: LpadcConfig, - ) -> Self { + ) -> Result { let adc = Self::new_inner(_inst, pin, config); I::Interrupt::unpend(); @@ -176,28 +198,48 @@ impl<'a, I: Instance> Adc<'a, I> { adc } - /// Initialize ADC without interrupt support (for polling mode). + /// Read ADC value asynchronously. /// - /// # Arguments - /// * `_inst` - ADC peripheral instance - /// * `pin` - GPIO pin to use for ADC input - /// * `config` - ADC configuration - pub fn new_polling(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Self { - Self::new_inner(_inst, pin, config) + /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. + /// + /// The function: + /// 1. Enables the FIFO watermark interrupt + /// 2. Triggers a software conversion on trigger 0 + /// 3. Waits for the conversion to complete + /// 4. Returns the conversion result + /// + /// # Returns + /// 16-bit ADC conversion value + pub async fn read(&mut self) -> Result { + let wait = I::wait_cell().subscribe().await; + + Adc::<'a, I, Async>::enable_interrupt(self, 0x1); + Adc::<'a, I, Async>::do_software_trigger(self, 1); + + let _ = wait.await; + + let result = Adc::<'a, I, Async>::get_conv_result(self).unwrap().conv_value >> G_LPADC_RESULT_SHIFT; + Ok(result) } +} - /// Internal initialization function shared by `new` and `new_polling`. - fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Self { + + +impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { + /// Internal initialization function shared by `new_async` and `new_blocking`. + fn new_inner( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + config: LpadcConfig + ) -> Result { let adc = I::ptr(); - let _clock_freq = unsafe { - enable_and_reset::(&AdcConfig { + _ = unsafe { enable_and_reset::(&AdcConfig { power: config.power, source: config.source, div: config.div, }) - .expect("Adc Init should not fail") - }; + .map_err(Error::ClockSetup)? }; pin.mux(); @@ -282,7 +324,10 @@ impl<'a, I: Instance> Adc<'a, I> { // Enable ADC adc.ctrl().modify(|_, w| w.adcen().enabled()); - Self { _inst: PhantomData } + Ok(Self { + _inst: PhantomData, + _phantom: PhantomData, + }) } /// Deinitialize the ADC peripheral. @@ -508,13 +553,13 @@ impl<'a, I: Instance> Adc<'a, I> { /// /// # Returns /// - `Some(ConvResult)` if a result is available - /// - `Err(AdcError::FifoEmpty)` if the FIFO is empty - pub fn get_conv_result(&self) -> Result { + /// - `Err(Error::FifoEmpty)` if the FIFO is empty + pub fn get_conv_result(&self) -> Result { let adc = I::ptr(); let fifo = adc.resfifo0().read().bits(); const VALID_MASK: u32 = 1 << 31; if fifo & VALID_MASK == 0 { - return Err(AdcError::FifoEmpty); + return Err(Error::FifoEmpty); } Ok(ConvResult { @@ -524,30 +569,6 @@ impl<'a, I: Instance> Adc<'a, I> { conv_value: (fifo & 0xFFFF) as u16, }) } - - /// Read ADC value asynchronously. - /// - /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. - /// - /// The function: - /// 1. Enables the FIFO watermark interrupt - /// 2. Triggers a software conversion on trigger 0 - /// 3. Waits for the conversion to complete - /// 4. Returns the conversion result - /// - /// # Returns - /// 16-bit ADC conversion value - pub async fn read(&mut self) -> Result { - let wait = I::wait_cell().subscribe().await; - - self.enable_interrupt(0x1); - self.do_software_trigger(1); - - let _ = wait.await; - - let result = self.get_conv_result().unwrap().conv_value >> G_LPADC_RESULT_SHIFT; - Ok(result) - } } impl Handler for InterruptHandler { @@ -607,6 +628,20 @@ pub trait AdcPin: GpioPin + sealed::Sealed + PeripheralType { fn mux(&self); } +/// Driver mode. +#[allow(private_bounds)] +pub trait ModeAdc: sealed::Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl sealed::Sealed for Blocking {} +impl ModeAdc for Blocking {} + +/// Async mode. +pub struct Async; +impl sealed::Sealed for Async {} +impl ModeAdc for Async {} + macro_rules! impl_pin { ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { impl $trait for crate::peripherals::$pin { diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index a0e392634..67fcf6361 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { source: AdcClockSel::FroLfDiv, div: Div4::no_div(), }; - let mut adc = Adc::new(p.ADC1, p.P1_10, Irqs, adc_config); + let mut adc = Adc::new_async(p.ADC1, p.P1_10, Irqs, adc_config).unwrap(); adc.do_offset_calibration(); adc.do_auto_calibration(); diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 65b66c8dd..14d47329a 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -2,7 +2,7 @@ #![no_main] use embassy_executor::Spawner; -use hal::adc::{Adc, ConvResult, LpadcConfig, TriggerPriorityPolicy}; +use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; use hal::clocks::PoweredClock; use hal::config::Config; use hal::clocks::config::Div8; @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { source: AdcClockSel::FroLfDiv, div: Div4::no_div(), }; - let adc = Adc::new_polling(p.ADC1, p.P1_10, adc_config); + let adc = Adc::new_blocking(p.ADC1, p.P1_10, adc_config).unwrap(); adc.do_offset_calibration(); adc.do_auto_calibration(); @@ -58,11 +58,15 @@ async fn main(_spawner: Spawner) { loop { adc.do_software_trigger(1); - let mut result: Option = None; - while result.is_none() { - result = adc.get_conv_result(); - } - let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; - defmt::info!("value: {=u16}", value); + let result = loop { + match adc.get_conv_result() { + Ok(res) => break res, + Err(_) => { + // Conversion not ready, continue polling + } + } + }; + let value = result.conv_value >> G_LPADC_RESULT_SHIFT; + defmt::info!("ADC value: {=u16}", value); } } -- cgit From 20ed25fbad4e7651316b4841aefdf62ee197ea31 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 15:17:54 -0800 Subject: set_conv_command_config returns Result instead of panic if index > 7 Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 14 ++++++++++++-- examples/mcxa/src/bin/adc_interrupt.rs | 2 +- examples/mcxa/src/bin/adc_polling.rs | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 16b40fa45..af7191573 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -435,9 +435,17 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// # Arguments /// * `index` - Command index /// * `config` - Command configuration - pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { + /// + /// # Returns + /// * `Ok(())` if the command was configured successfully + /// * `Err(Error::InvalidConfig)` if the index is out of range + pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) -> Result<()> { let adc = I::ptr(); + if index < 1 || index > 7 { + return Err(Error::InvalidConfig); + } + macro_rules! write_cmd { ($idx:expr) => {{ paste! { @@ -475,8 +483,10 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { 5 => write_cmd!(5), 6 => write_cmd!(6), 7 => write_cmd!(7), - _ => panic!("Invalid command index: must be between 1 and 7"), + _ => unreachable!(), } + + Ok(()) } /// Get default conversion trigger configuration. diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 67fcf6361..257e04491 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) { let mut conv_command_config = adc.get_default_conv_command_config(); conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config); + adc.set_conv_command_config(1, &conv_command_config).unwrap(); let mut conv_trigger_config = adc.get_default_conv_trigger_config(); conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 14d47329a..b11b8957f 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { let mut conv_command_config = adc.get_default_conv_command_config(); conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config); + adc.set_conv_command_config(1, &conv_command_config).unwrap(); let mut conv_trigger_config = adc.get_default_conv_trigger_config(); conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; -- cgit From f81eeee3c1bfa1cab534181ff6349a0998fc26ea Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Mon, 8 Dec 2025 15:24:12 -0800 Subject: Use field accessors for FIFO results instead of hardcoded ones Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index af7191573..900a3a40a 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -143,9 +143,9 @@ pub enum Error { /// Contains the conversion value and metadata about the conversion. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvResult { - pub command_id_source: u32, - pub loop_count_index: u32, - pub trigger_id_source: u32, + pub command_id_source: u8, + pub loop_count_index: u8, + pub trigger_id_source: u8, pub conv_value: u16, } @@ -566,17 +566,16 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// - `Err(Error::FifoEmpty)` if the FIFO is empty pub fn get_conv_result(&self) -> Result { let adc = I::ptr(); - let fifo = adc.resfifo0().read().bits(); - const VALID_MASK: u32 = 1 << 31; - if fifo & VALID_MASK == 0 { + let fifo = adc.resfifo0().read(); + if !fifo.valid().is_valid() { return Err(Error::FifoEmpty); } Ok(ConvResult { - command_id_source: (fifo >> 24) & 0x0F, - loop_count_index: (fifo >> 20) & 0x0F, - trigger_id_source: (fifo >> 16) & 0x0F, - conv_value: (fifo & 0xFFFF) as u16, + command_id_source: fifo.cmdsrc().bits(), + loop_count_index: fifo.loopcnt().bits(), + trigger_id_source: fifo.tsrc().bits(), + conv_value: fifo.d().bits(), }) } } -- cgit From bea34ace6b4d8af29d2deb169ddb3d7c258498ee Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Tue, 9 Dec 2025 09:39:27 -0800 Subject: Add support of ADC2 and ADC3 instances Signed-off-by: Mathis Deroo --- embassy-mcxa/Cargo.toml | 2 +- embassy-mcxa/src/adc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml index 8ed842aec..d1e40ab0a 100644 --- a/embassy-mcxa/Cargo.toml +++ b/embassy-mcxa/Cargo.toml @@ -39,7 +39,7 @@ embedded-hal-nb = { version = "1.0" } embedded-io = "0.6" embedded-io-async = { version = "0.6.1" } heapless = "0.8" -mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "e7dfed8740b449b6ac646bab8ac6776a3c099267" } +mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "ecb2af952d94a3bd3db7f9108e2258767eea4f97" } nb = "1.1.0" paste = "1.0.15" maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] } diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 900a3a40a..f4ac133d7 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -630,7 +630,7 @@ macro_rules! impl_instance { }; } -impl_instance!(0, 1); //ADC2 and ADC3 missing in the PAC ?? +impl_instance!(0, 1, 2, 3); pub trait AdcPin: GpioPin + sealed::Sealed + PeripheralType { /// Set the given pin to the correct muxing state -- cgit From 06c16ca54dc846efa4b256620b4658a5b8378f81 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Tue, 9 Dec 2025 10:42:30 -0800 Subject: Fix cmdlX and cmdhX access using updated PAC Signed-off-by: Mathis Deroo --- embassy-mcxa/Cargo.toml | 2 +- embassy-mcxa/src/adc.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml index d1e40ab0a..cb985a2e9 100644 --- a/embassy-mcxa/Cargo.toml +++ b/embassy-mcxa/Cargo.toml @@ -39,7 +39,7 @@ embedded-hal-nb = { version = "1.0" } embedded-io = "0.6" embedded-io-async = { version = "0.6.1" } heapless = "0.8" -mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "ecb2af952d94a3bd3db7f9108e2258767eea4f97" } +mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "02bd04a21ef8f8f67f88239ff5df765bb7e60fd8" } nb = "1.1.0" paste = "1.0.15" maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] } diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index f4ac133d7..abcdf86f8 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -449,23 +449,23 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { macro_rules! write_cmd { ($idx:expr) => {{ paste! { - adc.[]().write(|w| unsafe { + adc.[]().write(|w| { w.adch() - .bits(config.channel_number.into()) + .variant(config.channel_number) .mode() - .bit(config.conversion_resolution_mode.into()) + .variant(config.conversion_resolution_mode) }); adc.[]().write(|w| unsafe { w.next() - .bits(config.chained_next_command_number.into()) + .variant(config.chained_next_command_number) .loop_() .bits(config.loop_count) .avgs() - .bits(config.hardware_average_mode.into()) + .variant(config.hardware_average_mode) .sts() - .bits(config.sample_time_mode.into()) + .variant(config.sample_time_mode) .cmpen() - .bits(config.hardware_compare_mode.into()) + .variant(config.hardware_compare_mode) .wait_trig() .bit(config.enable_wait_trigger) .lwi() -- cgit From b8b0bb6eee22827b4eb493c6b48b35e01b860aad Mon Sep 17 00:00:00 2001 From: Cesar Tamayo Claro Date: Tue, 9 Dec 2025 13:44:23 -0700 Subject: Add basic RTC support --- examples/stm32f3/Cargo.toml | 5 ++-- examples/stm32wba6/src/bin/rtc.rs | 63 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 examples/stm32wba6/src/bin/rtc.rs diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 4349e8055..e624b4092 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] # Change stm32f303ze to your chip name, if necessary. -embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } +embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f302r8", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } @@ -27,7 +27,8 @@ embedded-storage = "0.3.1" static_cell = "2" [profile.release] -debug = 2 +debug = false +opt-level = 'z' [package.metadata.embassy] build = [ diff --git a/examples/stm32wba6/src/bin/rtc.rs b/examples/stm32wba6/src/bin/rtc.rs new file mode 100644 index 000000000..1e5634c60 --- /dev/null +++ b/examples/stm32wba6/src/bin/rtc.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] + +// use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_stm32::rcc::*; +use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +pub fn pll_init(config: &mut Config) { + config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz + mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO + divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) + // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) + divq: None, + divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) + frac: Some(0), // Fractional part (enabled) + }); + + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.apb7_pre = APBPrescaler::DIV1; + config.rcc.ahb5_pre = AHB5Prescaler::DIV4; + + // voltage scale for max performance + config.rcc.voltage_scale = VoltageScale::RANGE1; + // route PLL1_P into the USB‐OTG‐HS block + config.rcc.sys = Sysclk::PLL1_R; +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + pll_init(&mut config); + + let p = embassy_stm32::init(config); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + // Setting datetime + let initial_datetime = DateTime::from(2025, 12, 9, DayOfWeek::Tuesday, 11, 00, 00, 0).unwrap(); + match rtc.0.set_datetime(initial_datetime) { + Ok(()) => info!("RTC set successfully."), + Err(e) => error!("Failed to set RTC date/time: {:?}", e), + } + + // Reading datetime every 1s + loop { + match rtc.1.now() { + Ok(result) => info!("{}", result), + Err(e) => error!("Failed to set RTC date/time: {:?}", e), + } + + Timer::after_millis(1000).await; + } +} -- cgit From e8a84adb8d5bf3f773dfccd88c97390f36b5605a Mon Sep 17 00:00:00 2001 From: Cesar Tamayo Claro Date: Tue, 9 Dec 2025 14:03:55 -0700 Subject: Undo smt32f3 accidental change --- examples/stm32f3/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index e624b4092..a4cfe7ed8 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] # Change stm32f303ze to your chip name, if necessary. -embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f302r8", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } +embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -- cgit From 351f300fa3be839dfddb409c764541746d19b939 Mon Sep 17 00:00:00 2001 From: Cesar Tamayo Claro Date: Tue, 9 Dec 2025 14:05:28 -0700 Subject: Undo smt32f3 accidental change 2.0 --- examples/stm32f3/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index a4cfe7ed8..4349e8055 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -27,8 +27,7 @@ embedded-storage = "0.3.1" static_cell = "2" [profile.release] -debug = false -opt-level = 'z' +debug = 2 [package.metadata.embassy] build = [ -- cgit From dfdd0432ef41de44a8598845822e74138479e40a Mon Sep 17 00:00:00 2001 From: Simon Börjesson Date: Tue, 9 Dec 2025 22:14:27 +0100 Subject: Fix formatting --- cyw43/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index d723037c7..73d2830ca 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -31,7 +31,7 @@ use ioctl::IoctlState; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; pub use crate::control::{ - AddMulticastAddressError, Control, JoinError, JoinAuth, JoinOptions, ScanOptions, ScanType, Scanner, + AddMulticastAddressError, Control, JoinAuth, JoinError, JoinOptions, ScanOptions, ScanType, Scanner, }; pub use crate::runner::Runner; pub use crate::structs::BssInfo; -- cgit From 4f0eb421de9e08bbbf7f9a58f8b29c451de59894 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Tue, 9 Dec 2025 15:13:07 -0800 Subject: run rustfmt Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 30 ++++++++++-------------------- examples/mcxa/src/bin/adc_interrupt.rs | 11 +++++------ examples/mcxa/src/bin/adc_polling.rs | 4 ++-- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index abcdf86f8..6500754ba 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -10,7 +10,7 @@ use maitake_sync::WaitCell; use paste::paste; use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; -use crate::clocks::{Gate, PoweredClock, enable_and_reset, ClockError}; +use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; @@ -18,7 +18,6 @@ use crate::pac::adc1::cmdl1::{Adch, Mode}; use crate::pac::adc1::ctrl::CalAvgs; use crate::pac::adc1::tctrl::{Tcmd, Tpri}; - const G_LPADC_RESULT_SHIFT: u32 = 0; /// Trigger priority policy for ADC conversions. @@ -166,12 +165,7 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { /// * `_inst` - ADC peripheral instance /// * `pin` - GPIO pin to use for ADC /// * `config` - ADC configuration - pub fn new_blocking( - _inst: Peri<'a, I>, - pin: Peri<'a, - impl AdcPin>, - config: LpadcConfig - ) -> Result { + pub fn new_blocking(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Result { Self::new_inner(_inst, pin, config) } } @@ -223,23 +217,19 @@ impl<'a, I: Instance> Adc<'a, I, Async> { } } - - impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// Internal initialization function shared by `new_async` and `new_blocking`. - fn new_inner( - _inst: Peri<'a, I>, - pin: Peri<'a, impl AdcPin>, - config: LpadcConfig - ) -> Result { + fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Result { let adc = I::ptr(); - _ = unsafe { enable_and_reset::(&AdcConfig { + _ = unsafe { + enable_and_reset::(&AdcConfig { power: config.power, source: config.source, div: config.div, }) - .map_err(Error::ClockSetup)? }; + .map_err(Error::ClockSetup)? + }; pin.mux(); @@ -324,9 +314,9 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { // Enable ADC adc.ctrl().modify(|_, w| w.adcen().enabled()); - Ok(Self { + Ok(Self { _inst: PhantomData, - _phantom: PhantomData, + _phantom: PhantomData, }) } @@ -435,7 +425,7 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// # Arguments /// * `index` - Command index /// * `config` - Command configuration - /// + /// /// # Returns /// * `Ok(())` if the command was configured successfully /// * `Err(Error::InvalidConfig)` if the index is out of range diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 257e04491..5876923a1 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -2,16 +2,16 @@ #![no_main] use embassy_executor::Spawner; -use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy, InterruptHandler}; +use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; +use hal::bind_interrupts; use hal::clocks::PoweredClock; -use hal::config::Config; use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::config::Config; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; use hal::pac::adc1::tctrl::Tcmd; -use hal::bind_interrupts; use hal::peripherals::ADC1; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -19,12 +19,11 @@ bind_interrupts!(struct Irqs { ADC1 => InterruptHandler; }); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); - + let p = hal::init(config); defmt::info!("ADC interrupt Example"); @@ -62,7 +61,7 @@ async fn main(_spawner: Spawner) { defmt::info!("ADC configuration done..."); loop { - match adc.read().await { + match adc.read().await { Ok(value) => { defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); } diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index b11b8957f..d048bb56f 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -4,9 +4,9 @@ use embassy_executor::Spawner; use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; use hal::clocks::PoweredClock; -use hal::config::Config; use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::config::Config; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; @@ -19,7 +19,7 @@ const G_LPADC_RESULT_SHIFT: u32 = 0; async fn main(_spawner: Spawner) { let mut config = Config::default(); config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); - + let p = hal::init(config); defmt::info!("=== ADC polling Example ==="); -- cgit From 27cfd967180d6163a082ffe1ded17a6932cac75c Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 9 Dec 2025 19:35:26 -0600 Subject: dma: add functions --- embassy-stm32/src/dma/util.rs | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 3245887c1..304268963 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs @@ -20,6 +20,16 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) } + #[allow(dead_code)] + pub unsafe fn read_unchecked<'a, W: Word>( + &'a self, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_read(self.channel.clone_unchecked(), self.request, peri_addr, buf, options) + } + pub unsafe fn read_raw<'a, MW: Word, PW: Word>( &'a mut self, peri_addr: *mut PW, @@ -29,6 +39,16 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) } + #[allow(dead_code)] + pub unsafe fn read_raw_unchecked<'a, MW: Word, PW: Word>( + &'a self, + peri_addr: *mut PW, + buf: *mut [MW], + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_read_raw(self.channel.clone_unchecked(), self.request, peri_addr, buf, options) + } + pub unsafe fn write<'a, W: Word>( &'a mut self, buf: &'a [W], @@ -38,6 +58,16 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) } + #[allow(dead_code)] + pub unsafe fn write_unchecked<'a, W: Word>( + &'a self, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write(self.channel.clone_unchecked(), self.request, buf, peri_addr, options) + } + pub unsafe fn write_raw<'a, MW: Word, PW: Word>( &'a mut self, buf: *const [MW], @@ -47,6 +77,16 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_write_raw(self.channel.reborrow(), self.request, buf, peri_addr, options) } + #[allow(dead_code)] + pub unsafe fn write_raw_unchecked<'a, MW: Word, PW: Word>( + &'a self, + buf: *const [MW], + peri_addr: *mut PW, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write_raw(self.channel.clone_unchecked(), self.request, buf, peri_addr, options) + } + #[allow(dead_code)] pub unsafe fn write_repeated<'a, W: Word>( &'a mut self, @@ -64,4 +104,22 @@ impl<'d> ChannelAndRequest<'d> { options, ) } + + #[allow(dead_code)] + pub unsafe fn write_repeated_unchecked<'a, W: Word>( + &'a self, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write_repeated( + self.channel.clone_unchecked(), + self.request, + repeated, + count, + peri_addr, + options, + ) + } } -- cgit From c5c7a2143b15530b0c8f08d1dd6e24a6985318f9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 9 Dec 2025 19:36:05 -0600 Subject: sdmmc: refactor sd card creation --- embassy-stm32/src/sdmmc/mod.rs | 285 +++++++++++++++----------------------- examples/stm32f4/src/bin/sdmmc.rs | 35 ++--- examples/stm32f7/src/bin/sdmmc.rs | 12 +- examples/stm32h7/src/bin/sdmmc.rs | 14 +- tests/stm32/src/bin/sdmmc.rs | 37 ++--- 5 files changed, 159 insertions(+), 224 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index f862d73b1..ad00b4398 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -164,7 +164,9 @@ pub enum Error { StBitErr, } -pub trait Addressable: Sized { +/// Represents either an SD or EMMC card +pub trait Addressable: Sized + Clone { + /// Associated type type Ext; /// Get this peripheral's address on the SDMMC bus @@ -175,41 +177,60 @@ pub trait Addressable: Sized { /// Size in bytes fn size(&self) -> u64; +} - async fn write_block<'a>( - &mut self, - sdmmc: &mut Sdmmc<'a>, - block_idx: u32, - buffer: &DataBlock, - ) -> Result<(), Error> { - sdmmc.write_block(self, block_idx, buffer).await +/// Storage Device +pub struct StorageDevice<'a, 'b, T: Addressable> { + info: T, + /// Inner member + pub sdmmc: &'a mut Sdmmc<'b>, +} + +/// Card Storage Device +impl<'a, 'b> StorageDevice<'a, 'b, Card> { + /// Create a new SD card + pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { + let info = sdmmc.init_sd_card(cmd_block, freq).await?; + + Ok(Self { info, sdmmc }) } +} - async fn write_blocks<'a>( - &mut self, - sdmmc: &mut Sdmmc<'a>, - block_idx: u32, - blocks: &[DataBlock], - ) -> Result<(), Error> { - sdmmc.write_blocks(self, block_idx, blocks).await +/// Emmc storage device +impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { + /// Create a new EMMC card + pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { + let info = sdmmc.init_emmc(cmd_block, freq).await?; + + Ok(Self { info, sdmmc }) } +} - async fn read_block<'a>( - &mut self, - sdmmc: &mut Sdmmc<'a>, - block_idx: u32, - buffer: &mut DataBlock, - ) -> Result<(), Error> { - sdmmc.read_block(self, block_idx, buffer).await +/// Card or Emmc storage device +impl<'a, 'b, T: Addressable> StorageDevice<'a, 'b, T> { + /// Write a block + pub fn card(&self) -> T { + self.info.clone() } - async fn read_blocks<'a>( - &mut self, - sdmmc: &mut Sdmmc<'a>, - block_idx: u32, - blocks: &mut [DataBlock], - ) -> Result<(), Error> { - sdmmc.read_blocks(self, block_idx, blocks).await + /// Write a block + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + self.sdmmc.write_block(&mut self.info, block_idx, buffer).await + } + + /// Write a block + pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { + self.sdmmc.write_blocks(&mut self.info, block_idx, blocks).await + } + + /// Read a block + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + self.sdmmc.read_block(&mut self.info, block_idx, buffer).await + } + + /// Read a block + pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { + self.sdmmc.read_blocks(&mut self.info, block_idx, blocks).await } } @@ -240,7 +261,7 @@ impl Card { /// frequency to be > 12.5MHz. /// /// SD only. - pub async fn switch_signalling_mode<'a>( + async fn switch_signalling_mode<'a>( &mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock, @@ -262,14 +283,7 @@ impl Card { // Arm `OnDrop` after the buffer, so it will be dropped first let on_drop = OnDrop::new(|| sdmmc.on_drop()); - let transfer = sdmmc.prepare_datapath_read( - &sdmmc.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - cmd_block.as_mut(), - 64, - 6, - ); + let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, cmd_block.as_mut(), 64, 6); sdmmc.enable_interrupts(); sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 @@ -308,7 +322,7 @@ impl Card { /// Reads the SCR register. /// /// SD only. - pub async fn get_scr<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { + async fn get_scr<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { // Read the 64-bit SCR register sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 sdmmc.cmd(common_cmd::app_cmd(self.rca), false)?; @@ -318,14 +332,7 @@ impl Card { // Arm `OnDrop` after the buffer, so it will be dropped first let on_drop = OnDrop::new(|| sdmmc.on_drop()); - let transfer = sdmmc.prepare_datapath_read( - &sdmmc.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - scr, - 8, - 3, - ); + let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, scr, 8, 3); sdmmc.enable_interrupts(); sdmmc.cmd(sd_cmd::send_scr(), true)?; @@ -347,7 +354,7 @@ impl Card { /// Reads the SD Status (ACMD13) /// /// SD only. - pub async fn read_sd_status<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { + async fn read_sd_status<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { let rca = self.rca; sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 @@ -358,14 +365,7 @@ impl Card { // Arm `OnDrop` after the buffer, so it will be dropped first let on_drop = OnDrop::new(|| sdmmc.on_drop()); - let transfer = sdmmc.prepare_datapath_read( - &sdmmc.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - status.as_mut(), - 64, - 6, - ); + let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, status.as_mut(), 64, 6); sdmmc.enable_interrupts(); sdmmc.cmd(sd_cmd::sd_status(), true)?; @@ -434,14 +434,7 @@ impl Emmc { sdmmc.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 - let transfer = sdmmc.prepare_datapath_read( - &sdmmc.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - buffer, - 512, - 9, - ); + let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, buffer, 512, 9); sdmmc.enable_interrupts(); sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; @@ -604,42 +597,6 @@ impl SdmmcPeripheral { Self::Emmc(e) => e.rca, } } - /// Is this a standard or high capacity peripheral? - fn get_capacity(&self) -> CardCapacity { - match self { - Self::SdCard(c) => c.card_type, - Self::Emmc(e) => e.capacity, - } - } - /// Size in bytes - fn size(&self) -> u64 { - match self { - // SDHC / SDXC / SDUC - Self::SdCard(c) => u64::from(c.csd.block_count()) * 512, - // capacity > 2GB - Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512, - } - } - - /// Get a mutable reference to the SD Card. - /// - /// Panics if there is another peripheral instead. - fn get_sd_card(&mut self) -> &mut Card { - match *self { - Self::SdCard(ref mut c) => c, - _ => unreachable!("SD only"), - } - } - - /// Get a mutable reference to the eMMC. - /// - /// Panics if there is another peripheral instead. - fn get_emmc(&mut self) -> &mut Emmc { - match *self { - Self::Emmc(ref mut e) => e, - _ => unreachable!("eMMC only"), - } - } } /// Sdmmc device @@ -1037,9 +994,8 @@ impl<'d> Sdmmc<'d> { /// `buffer` must be valid for the whole transfer and word aligned #[allow(unused_variables)] fn prepare_datapath_read<'a>( - &self, + &'a self, config: &Config, - #[cfg(sdmmc_v1)] dma: &'a mut ChannelAndRequest<'d>, buffer: &'a mut [u32], length_bytes: u32, block_size: u8, @@ -1053,8 +1009,12 @@ impl<'d> Sdmmc<'d> { regs.dlenr().write(|w| w.set_datalength(length_bytes)); + // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] - let transfer = unsafe { dma.read(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) }; + let transfer = unsafe { + self.dma + .read_unchecked(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) + }; #[cfg(sdmmc_v2)] let transfer = { regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); @@ -1080,7 +1040,7 @@ impl<'d> Sdmmc<'d> { /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - fn prepare_datapath_write<'a>(&self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { + fn prepare_datapath_write<'a>(&'a self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); let regs = self.info.regs; @@ -1090,10 +1050,11 @@ impl<'d> Sdmmc<'d> { regs.dlenr().write(|w| w.set_datalength(length_bytes)); + // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] let transfer = unsafe { self.dma - .write(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) + .write_unchecked(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) }; #[cfg(sdmmc_v2)] let transfer = { @@ -1374,14 +1335,7 @@ impl<'d> Sdmmc<'d> { let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = self.prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - buffer, - 512, - 9, - ); + let transfer = self.prepare_datapath_read(&self.config, buffer, 512, 9); self.enable_interrupts(); self.cmd(common_cmd::read_single_block(address), true)?; @@ -1423,14 +1377,7 @@ impl<'d> Sdmmc<'d> { let on_drop = OnDrop::new(|| self.on_drop()); - let transfer = self.prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - buffer, - 512 * blocks.len() as u32, - 9, - ); + let transfer = self.prepare_datapath_read(&self.config, buffer, 512 * blocks.len() as u32, 9); self.enable_interrupts(); self.cmd(common_cmd::read_multiple_blocks(address), true)?; @@ -1588,6 +1535,8 @@ impl<'d> Sdmmc<'d> { freq: Hertz, card: &mut SdmmcPeripheral, ) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); + let regs = self.info.regs; let bus_width = match (self.d3.is_some(), self.d7.is_some()) { @@ -1824,9 +1773,7 @@ impl<'d> Sdmmc<'d> { /// Initializes card (if present) and sets the bus at the specified frequency. /// /// SD only. - pub async fn init_sd_card(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let _scoped_block_stop = self.info.rcc.block_stop(); - + async fn init_sd_card(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { let mut card = SdmmcPeripheral::SdCard(Card::default()); self.init_internal(cmd_block, freq, &mut card).await?; @@ -1841,9 +1788,7 @@ impl<'d> Sdmmc<'d> { /// Initializes eMMC and sets the bus at the specified frequency. /// /// eMMC only. - pub async fn init_emmc(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let _scoped_block_stop = self.info.rcc.block_stop(); - + async fn init_emmc(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { let mut card = SdmmcPeripheral::Emmc(Emmc::default()); self.init_internal(cmd_block, freq, &mut card).await?; @@ -1960,47 +1905,45 @@ foreach_peripheral!( }; ); -// impl<'d, A: Addressable> block_device_driver::BlockDevice<512> for Sdmmc<'d>, A { -// type Error = Error; -// type Align = aligned::A4; -// -// async fn read( -// &mut self, -// block_address: u32, -// buf: &mut [aligned::Aligned], -// ) -> Result<(), Self::Error> { -// let _scoped_block_stop = self.info.rcc.block_stop(); -// // TODO: I think block_address needs to be adjusted by the partition start offset -// if buf.len() == 1 { -// let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; -// self.read_block(block_address, block).await?; -// } else { -// let blocks: &mut [DataBlock] = -// unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; -// self.read_blocks(block_address, blocks).await?; -// } -// Ok(()) -// } -// -// async fn write( -// &mut self, -// block_address: u32, -// buf: &[aligned::Aligned], -// ) -> Result<(), Self::Error> { -// let _scoped_block_stop = self.info.rcc.block_stop(); -// // TODO: I think block_address needs to be adjusted by the partition start offset -// if buf.len() == 1 { -// let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; -// self.write_block(block_address, block).await?; -// } else { -// let blocks: &[DataBlock] = -// unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; -// self.write_blocks(block_address, blocks).await?; -// } -// Ok(()) -// } -// -// async fn size(&mut self) -> Result { -// Ok(self.card()?.size()) -// } -// } +impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { + type Error = Error; + type Align = aligned::A4; + + async fn read( + &mut self, + block_address: u32, + buf: &mut [aligned::Aligned], + ) -> Result<(), Self::Error> { + // TODO: I think block_address needs to be adjusted by the partition start offset + if buf.len() == 1 { + let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; + self.read_block(block_address, block).await?; + } else { + let blocks: &mut [DataBlock] = + unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; + self.read_blocks(block_address, blocks).await?; + } + Ok(()) + } + + async fn write( + &mut self, + block_address: u32, + buf: &[aligned::Aligned], + ) -> Result<(), Self::Error> { + // TODO: I think block_address needs to be adjusted by the partition start offset + if buf.len() == 1 { + let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; + self.write_block(block_address, block).await?; + } else { + let blocks: &[DataBlock] = + unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; + self.write_blocks(block_address, blocks).await?; + } + Ok(()) + } + + async fn size(&mut self) -> Result { + Ok(self.info.size()) + } +} diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index fe0f887bf..897b0e05e 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; +use embassy_stm32::sdmmc::{CmdBlock, DataBlock, Sdmmc, StorageDevice}; use embassy_stm32::time::{Hertz, mhz}; use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; @@ -57,23 +57,16 @@ async fn main(_spawner: Spawner) { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - let mut err = None; - loop { - match sdmmc.init_sd_card(mhz(24)).await { - Ok(_) => break, - Err(e) => { - if err != Some(e) { - info!("waiting for card error, retrying: {:?}", e); - err = Some(e); - } - } - } - } + let mut cmd_block = CmdBlock::new(); + + let mut storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(24)) + .await + .unwrap(); - let card = unwrap!(sdmmc.card()); + let card = storage.card(); - info!("Card: {:#?}", Debug2Format(card)); - info!("Clock: {}", sdmmc.clock()); + info!("Card: {:#?}", Debug2Format(&card)); + info!("Clock: {}", storage.sdmmc.clock()); // Arbitrary block index let block_idx = 16; @@ -81,7 +74,7 @@ async fn main(_spawner: Spawner) { // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. let mut block = DataBlock([0u8; 512]); - sdmmc.read_block(block_idx, &mut block).await.unwrap(); + storage.read_block(block_idx, &mut block).await.unwrap(); info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); if !ALLOW_WRITES { @@ -91,17 +84,17 @@ async fn main(_spawner: Spawner) { info!("Filling block with 0x55"); block.fill(0x55); - sdmmc.write_block(block_idx, &block).await.unwrap(); + storage.write_block(block_idx, &block).await.unwrap(); info!("Write done"); - sdmmc.read_block(block_idx, &mut block).await.unwrap(); + storage.read_block(block_idx, &mut block).await.unwrap(); info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); info!("Filling block with 0xAA"); block.fill(0xAA); - sdmmc.write_block(block_idx, &block).await.unwrap(); + storage.write_block(block_idx, &block).await.unwrap(); info!("Write done"); - sdmmc.read_block(block_idx, &mut block).await.unwrap(); + storage.read_block(block_idx, &mut block).await.unwrap(); info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); } diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 8809b5d0c..0dd4dd6f3 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::{CmdBlock, Sdmmc, StorageDevice}; use embassy_stm32::time::{Hertz, mhz}; use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; @@ -54,9 +54,13 @@ async fn main(_spawner: Spawner) { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_sd_card(mhz(25)).await); + let mut cmd_block = CmdBlock::new(); - let card = unwrap!(sdmmc.card()); + let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25)) + .await + .unwrap(); - info!("Card: {:#?}", Debug2Format(card)); + let card = storage.card(); + + info!("Card: {:#?}", Debug2Format(&card)); } diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 4977fec79..548d0bd09 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::{CmdBlock, Sdmmc, StorageDevice}; use embassy_stm32::time::mhz; use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; @@ -13,7 +13,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { let mut config = Config::default(); { use embassy_stm32::rcc::*; @@ -53,11 +53,13 @@ async fn main(_spawner: Spawner) -> ! { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_sd_card(mhz(25)).await); + let mut cmd_block = CmdBlock::new(); - let card = unwrap!(sdmmc.card()); + let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25)) + .await + .unwrap(); - info!("Card: {:#?}", Debug2Format(card)); + let card = storage.card(); - loop {} + info!("Card: {:#?}", Debug2Format(&card)); } diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 9f9c526e1..454d0dd57 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -7,7 +7,7 @@ mod common; use common::*; use defmt::assert_eq; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; +use embassy_stm32::sdmmc::{CmdBlock, DataBlock, Sdmmc, StorageDevice}; use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; @@ -54,43 +54,36 @@ async fn main(_spawner: Spawner) { Default::default(), ); - let mut err = None; - loop { - match s.init_sd_card(mhz(24)).await { - Ok(_) => break, - Err(e) => { - if err != Some(e) { - info!("waiting for card: {:?}", e); - err = Some(e); - } - } - } - } + let mut cmd_block = CmdBlock::new(); - let card = unwrap!(s.card()); + let mut storage = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(24)) + .await + .unwrap(); - info!("Card: {:#?}", Debug2Format(card)); - info!("Clock: {}", s.clock()); + let card = storage.card(); + + info!("Card: {:#?}", Debug2Format(&card)); + info!("Clock: {}", storage.sdmmc.clock()); info!("writing pattern1..."); - s.write_block(block_idx, &pattern1).await.unwrap(); + storage.write_block(block_idx, &pattern1).await.unwrap(); info!("reading..."); - s.read_block(block_idx, &mut block).await.unwrap(); + storage.read_block(block_idx, &mut block).await.unwrap(); assert_eq!(block, pattern1); info!("writing pattern2..."); - s.write_block(block_idx, &pattern2).await.unwrap(); + storage.write_block(block_idx, &pattern2).await.unwrap(); info!("reading..."); - s.read_block(block_idx, &mut block).await.unwrap(); + storage.read_block(block_idx, &mut block).await.unwrap(); assert_eq!(block, pattern2); info!("writing blocks [pattern1, pattern2]..."); - s.write_blocks(block_idx, &patterns).await.unwrap(); + storage.write_blocks(block_idx, &patterns).await.unwrap(); info!("reading blocks..."); - s.read_blocks(block_idx, &mut blocks).await.unwrap(); + storage.read_blocks(block_idx, &mut blocks).await.unwrap(); assert_eq!(&blocks, &patterns); drop(s); -- cgit From 31039201e7edcba6bbf6ba8679332cf98d6d7b1f Mon Sep 17 00:00:00 2001 From: Simon Börjesson Date: Wed, 10 Dec 2025 02:39:48 +0100 Subject: Update examples and tests --- cyw43/src/control.rs | 1 + examples/rp/src/bin/wifi_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_webrequest.rs | 2 +- tests/rp/src/bin/cyw43-perf.rs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 98f456ce4..36d5ee7f8 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -14,6 +14,7 @@ use crate::{PowerManagementMode, countries, events}; /// Join errors. #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum JoinError { /// Network not found. NetworkNotFound, diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index e39de4902..ef8d08b76 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -101,7 +101,7 @@ async fn main(spawner: Spawner) { .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) .await { - info!("join failed with status={}", err.status); + info!("join failed: {:?}", err); } info!("waiting for link..."); diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index ce85f4b9a..069afc794 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs @@ -106,7 +106,7 @@ async fn main(spawner: Spawner) { .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) .await { - info!("join failed with status={}", err.status); + info!("join failed: {:?}", err); } info!("waiting for link..."); diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 9487f5e1a..1ef9cf066 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -98,7 +98,7 @@ async fn main(spawner: Spawner) { { Ok(_) => break, Err(err) => { - panic!("join failed with status={}", err.status); + panic!("join failed: {:?}", err); } } } -- cgit From 972f87a661639549cdda39afaa4a3ee5f72bd2f0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 9 Dec 2025 19:57:25 -0600 Subject: tests/stm32: make usart more noise-immune --- tests/stm32/src/bin/usart.rs | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 0b98d3eeb..ef7efe96a 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -6,6 +6,7 @@ mod common; use common::*; use defmt::{assert, assert_eq, unreachable}; use embassy_executor::Spawner; +use embassy_stm32::mode::Blocking; use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; use embassy_time::{Duration, Instant, block_for}; @@ -24,22 +25,41 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap(); - // We can't send too many bytes, they have to fit in the FIFO. - // This is because we aren't sending+receiving at the same time. + let test_usart = async |usart: &mut Uart<'_, Blocking>| -> Result<(), Error> { + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. - let data = [0xC0, 0xDE]; - usart.blocking_write(&data).unwrap(); + let data = [0xC0, 0xDE]; + usart.blocking_write(&data)?; - let mut buf = [0; 2]; - usart.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, data); + let mut buf = [0; 2]; + usart.blocking_read(&mut buf)?; + assert_eq!(buf, data); - // Test flush doesn't hang. - usart.blocking_write(&data).unwrap(); - usart.blocking_flush().unwrap(); + // Test flush doesn't hang. + usart.blocking_write(&data)?; + usart.blocking_flush()?; - // Test flush doesn't hang if there's nothing to flush - usart.blocking_flush().unwrap(); + // Test flush doesn't hang if there's nothing to flush + usart.blocking_flush()?; + + Ok(()) + }; + + let mut is_ok = false; + for _ in 0..3 { + match test_usart(&mut usart).await { + Ok(()) => is_ok = true, + Err(Error::Noise) => is_ok = false, + Err(e) => defmt::panic!("{}", e), + } + + if is_ok { + break; + } + } + + assert!(is_ok); } // Test error handling with with an overflow error -- cgit From a48bd43f6ff39f9a98357a17d02ec03461b09351 Mon Sep 17 00:00:00 2001 From: Cesar Tamayo Claro Date: Tue, 9 Dec 2025 23:13:01 -0700 Subject: Update set date to Unix Epoch --- examples/stm32wba6/src/bin/rtc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32wba6/src/bin/rtc.rs b/examples/stm32wba6/src/bin/rtc.rs index 1e5634c60..f150df1c0 100644 --- a/examples/stm32wba6/src/bin/rtc.rs +++ b/examples/stm32wba6/src/bin/rtc.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); // Setting datetime - let initial_datetime = DateTime::from(2025, 12, 9, DayOfWeek::Tuesday, 11, 00, 00, 0).unwrap(); + let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap(); match rtc.0.set_datetime(initial_datetime) { Ok(()) => info!("RTC set successfully."), Err(e) => error!("Failed to set RTC date/time: {:?}", e), -- cgit From 1d4fb4e39ef13f5d2b1c8979e7058acf6836e85b Mon Sep 17 00:00:00 2001 From: Cesar Tamayo Claro Date: Tue, 9 Dec 2025 23:16:34 -0700 Subject: Remove commented import --- examples/stm32wba6/src/bin/rtc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32wba6/src/bin/rtc.rs b/examples/stm32wba6/src/bin/rtc.rs index f150df1c0..cef8501e0 100644 --- a/examples/stm32wba6/src/bin/rtc.rs +++ b/examples/stm32wba6/src/bin/rtc.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] -// use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; -- cgit From 3892f575a0b4fc8ad2186020389d9923568a2534 Mon Sep 17 00:00:00 2001 From: Cesar Tamayo Claro Date: Tue, 9 Dec 2025 23:17:03 -0700 Subject: Copy rtc into stm32wba examples folder --- examples/stm32wba/src/bin/rtc.rs | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 examples/stm32wba/src/bin/rtc.rs diff --git a/examples/stm32wba/src/bin/rtc.rs b/examples/stm32wba/src/bin/rtc.rs new file mode 100644 index 000000000..cef8501e0 --- /dev/null +++ b/examples/stm32wba/src/bin/rtc.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_stm32::rcc::*; +use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +pub fn pll_init(config: &mut Config) { + config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz + mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO + divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) + // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) + divq: None, + divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) + frac: Some(0), // Fractional part (enabled) + }); + + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.apb7_pre = APBPrescaler::DIV1; + config.rcc.ahb5_pre = AHB5Prescaler::DIV4; + + // voltage scale for max performance + config.rcc.voltage_scale = VoltageScale::RANGE1; + // route PLL1_P into the USB‐OTG‐HS block + config.rcc.sys = Sysclk::PLL1_R; +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + pll_init(&mut config); + + let p = embassy_stm32::init(config); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + // Setting datetime + let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap(); + match rtc.0.set_datetime(initial_datetime) { + Ok(()) => info!("RTC set successfully."), + Err(e) => error!("Failed to set RTC date/time: {:?}", e), + } + + // Reading datetime every 1s + loop { + match rtc.1.now() { + Ok(result) => info!("{}", result), + Err(e) => error!("Failed to set RTC date/time: {:?}", e), + } + + Timer::after_millis(1000).await; + } +} -- cgit From 62966d6fac20a7af7257e0a1a9015e1b7093b5ba Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 10 Dec 2025 15:02:57 +0100 Subject: rustfmt --- embassy-mcxa/src/adc.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 6500754ba..d7d17cf5f 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -2,16 +2,14 @@ use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; - -use crate::gpio::{GpioPin, SealedPin}; -use crate::interrupt::typelevel::{Handler, Interrupt}; -use crate::pac; use maitake_sync::WaitCell; use paste::paste; use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; - +use crate::gpio::{GpioPin, SealedPin}; +use crate::interrupt::typelevel::{Handler, Interrupt}; +use crate::pac; use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; use crate::pac::adc1::cmdl1::{Adch, Mode}; -- cgit From 8ef329e5cf1ce4d7a3ff6b6bec1551a0ae6d3e8b Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 10 Dec 2025 15:32:11 +0100 Subject: Add a basic "format all" script --- fmtall.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 fmtall.sh diff --git a/fmtall.sh b/fmtall.sh new file mode 100755 index 000000000..9cadcdbe9 --- /dev/null +++ b/fmtall.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -euo pipefail + +# We need the nightly toolchain for this +mv rust-toolchain-nightly.toml rust-toolchain.toml + +# Similar to the CI workflow, but don't just CHECK, actualy DO the formatting +find . -name '*.rs' -not -path '*target*' | xargs rustfmt --skip-children --unstable-features --edition 2024 + +# Put the toolchains back, copy back to nightly and do a clean checkout of rust-toolchain +mv rust-toolchain.toml rust-toolchain-nightly.toml +git checkout -- rust-toolchain.toml -- cgit From 64e23b79cce5c2b551110a4b8d446dbcb42ac3bd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 10 Dec 2025 15:58:28 +0100 Subject: nrf/cracen: fix bus fault on drop. Accessing the RNGCONTROL reg bus faults when ENABLE.RNG=0. Do all the enabling in start_rng/stop_rng, then do nothing on drop. This is fine now that we only have blocking RNG, we'll have to do something fancier in the future. --- embassy-nrf/src/cracen.rs | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/embassy-nrf/src/cracen.rs b/embassy-nrf/src/cracen.rs index 47ef1cd87..6381701c0 100644 --- a/embassy-nrf/src/cracen.rs +++ b/embassy-nrf/src/cracen.rs @@ -18,10 +18,7 @@ pub struct Cracen<'d, M: Mode> { impl<'d> Cracen<'d, Blocking> { /// Create a new CRACEN driver. pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self { - let me = Self { _peri, _p: PhantomData }; - - me.stop(); - me + Self { _peri, _p: PhantomData } } } @@ -48,7 +45,14 @@ impl<'d, M: Mode> Cracen<'d, M> { while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {} } - fn stop(&self) { + fn stop_rng(&self) { + let r = Self::core(); + r.rngcontrol().control().write(|w| { + w.set_enable(false); + }); + + while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {} + let r = Self::regs(); r.enable().write(|w| { w.set_cryptomaster(false); @@ -69,7 +73,7 @@ impl<'d, M: Mode> Cracen<'d, M> { chunk[..to_copy].copy_from_slice(&word[..to_copy]); } - self.stop(); + self.stop_rng(); } /// Generate a random u32 @@ -90,19 +94,7 @@ impl<'d, M: Mode> Cracen<'d, M> { impl<'d, M: Mode> Drop for Cracen<'d, M> { fn drop(&mut self) { - let r = Self::core(); - r.rngcontrol().control().write(|w| { - w.set_enable(false); - }); - - while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {} - - let r = Self::regs(); - r.enable().write(|w| { - w.set_cryptomaster(false); - w.set_rng(false); - w.set_pkeikg(false); - }); + // nothing to do here, since we stop+disable rng for each operation. } } -- cgit From 0a798fc533962c7094883d4ddbbf92abe621c7f3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 10 Dec 2025 12:29:03 -0800 Subject: MCXA: Add a function to read Reset Reason --- embassy-mcxa/src/lib.rs | 1 + embassy-mcxa/src/reset_reason.rs | 108 ++++++++++++++++++++++++++++++++++ examples/mcxa/src/bin/reset-reason.rs | 15 +++++ 3 files changed, 124 insertions(+) create mode 100644 embassy-mcxa/src/reset_reason.rs create mode 100644 examples/mcxa/src/bin/reset-reason.rs diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index be279e509..7de5839be 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -17,6 +17,7 @@ pub mod i2c; pub mod interrupt; pub mod lpuart; pub mod ostimer; +pub mod reset_reason; pub mod rtc; pub use crate::pac::NVIC_PRIO_BITS; diff --git a/embassy-mcxa/src/reset_reason.rs b/embassy-mcxa/src/reset_reason.rs new file mode 100644 index 000000000..1f5a0ec1f --- /dev/null +++ b/embassy-mcxa/src/reset_reason.rs @@ -0,0 +1,108 @@ +//! Reset reason +//! +//! MCXA families keep the most recent reset reason in the SRS +//! register of the CMC block. This lets users understand why the MCU +//! has reset and take appropriate corrective actions if required. + +/// Reads the most recent reset reason from the Core Mode Controller +/// (CMC). +pub fn reset_reason() -> ResetReason { + critical_section::with(|_| { + let regs = unsafe { &*crate::pac::Cmc::steal() }; + + let srs = regs.srs().read(); + + if srs.wakeup().is_enabled() { + ResetReason::WakeUp + } else if srs.por().bit_is_set() { + ResetReason::Por + } else if srs.vd().bit_is_set() { + ResetReason::VoltageDetect + } else if srs.warm().bit_is_set() { + ResetReason::Warm + } else if srs.fatal().bit_is_set() { + ResetReason::Fatal + } else if srs.pin().bit_is_set() { + ResetReason::Pin + } else if srs.dap().bit_is_set() { + ResetReason::Dap + } else if srs.rstack().bit_is_set() { + ResetReason::ResetAckTimeout + } else if srs.lpack().bit_is_set() { + ResetReason::LowPowerAckTimeout + } else if srs.scg().bit_is_set() { + ResetReason::SystemClockGeneration + } else if srs.wwdt0().bit_is_set() { + ResetReason::Wwdt0 + } else if srs.sw().bit_is_set() { + ResetReason::Software + } else if srs.lockup().bit_is_set() { + ResetReason::Lockup + } else if srs.cdog0().bit_is_set() { + ResetReason::Cdog0 + } else if srs.cdog1().bit_is_set() { + ResetReason::Cdog1 + } else if srs.jtag().bit_is_set() { + ResetReason::Jtag + } else { + ResetReason::Tamper + } + }) +} + +/// Indicates the type and source of the most recent reset. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum ResetReason { + /// Tamper reset. + Tamper, + + /// JTAG System Reset request. + Jtag, + + /// Code Watchdog 0 reset. + Cdog0, + + /// Code Watchdog 1 reset. + Cdog1, + + /// Lockup reset. + Lockup, + + /// Software reset. + Software, + + /// Windowed Watchdog 0 reset. + Wwdt0, + + /// System clock generation reset. + SystemClockGeneration, + + /// Low Power Acknowledge Timeout reset. + LowPowerAckTimeout, + + /// Reset Timeout. + ResetAckTimeout, + + /// Debug Access Port reset. + Dap, + + /// External assertion of RESET_b pin. + Pin, + + /// Fatal reset. + Fatal, + + /// Warm reset. + Warm, + + /// Voltage detect reset. + VoltageDetect, + + /// Power-on reset. + Por, + + /// Wake-up reset. + WakeUp, +} diff --git a/examples/mcxa/src/bin/reset-reason.rs b/examples/mcxa/src/bin/reset-reason.rs new file mode 100644 index 000000000..c244fbe04 --- /dev/null +++ b/examples/mcxa/src/bin/reset-reason.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use hal::config::Config; +use hal::reset_reason::reset_reason; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = Config::default(); + let _p = hal::init(config); + + defmt::info!("Reset Reason: '{}'", reset_reason()); +} -- cgit From 14079f148c2796889db04e1442181afa3165c5b6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 10 Dec 2025 16:28:04 -0600 Subject: sdmmc: more refactorign --- embassy-stm32/src/sdmmc/mod.rs | 1260 ++++++++++++++++++---------------------- tests/stm32/src/bin/sdmmc.rs | 50 +- 2 files changed, 625 insertions(+), 685 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ad00b4398..0d5260016 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -5,14 +5,16 @@ use core::default::Default; use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; +use core::slice; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use sdio_host::common_cmd::{self, Resp, ResponseLen}; +use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; 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::sd_cmd::{R6, R7}; use sdio_host::{Cmd, emmc_cmd, sd_cmd}; #[cfg(sdmmc_v1)] @@ -23,7 +25,7 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; -use crate::time::Hertz; +use crate::time::{Hertz, mhz}; use crate::{interrupt, peripherals}; /// Interrupt handler. @@ -60,6 +62,57 @@ impl interrupt::typelevel::Handler for InterruptHandl } } +struct U128(pub u128); + +trait TypedResp: Resp { + type Word: From; +} + +impl From for () { + fn from(value: U128) -> Self { + match value.0 { + 0 => (), + _ => unreachable!(), + } + } +} + +impl From for u32 { + fn from(value: U128) -> Self { + unwrap!(value.0.try_into()) + } +} + +impl From for u128 { + fn from(value: U128) -> Self { + value.0 + } +} + +impl TypedResp for Rz { + type Word = (); +} + +impl TypedResp for R1 { + type Word = u32; +} + +impl TypedResp for R2 { + type Word = u128; +} + +impl TypedResp for R3 { + type Word = u32; +} + +impl TypedResp for R6 { + type Word = u32; +} + +impl TypedResp for R7 { + type Word = u32; +} + /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. const SD_INIT_FREQ: Hertz = Hertz(400_000); @@ -104,6 +157,11 @@ impl DerefMut for DataBlock { } } +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) } +} + /// Command Block buffer for SDMMC command transfers. /// /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. @@ -190,70 +248,119 @@ pub struct StorageDevice<'a, 'b, T: Addressable> { impl<'a, 'b> StorageDevice<'a, 'b, Card> { /// Create a new SD card pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let info = sdmmc.init_sd_card(cmd_block, freq).await?; + let mut s = Self { + info: Card::default(), + sdmmc, + }; - Ok(Self { info, sdmmc }) + s.acquire(cmd_block, freq).await?; + + Ok(s) } -} -/// Emmc storage device -impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { - /// Create a new EMMC card - pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let info = sdmmc.init_emmc(cmd_block, freq).await?; + /// Initializes the card into a known state (or at least tries to). + pub async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let regs = self.sdmmc.info.regs; - Ok(Self { info, sdmmc }) - } -} + let _bus_width = match self.sdmmc.bus_width() { + BusWidth::Eight => return Err(Error::BusWidth), + bus_width => bus_width, + }; -/// Card or Emmc storage device -impl<'a, 'b, T: Addressable> StorageDevice<'a, 'b, T> { - /// Write a block - pub fn card(&self) -> T { - self.info.clone() - } + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + self.sdmmc.init_idle()?; - /// Write a block - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { - self.sdmmc.write_block(&mut self.info, block_idx, buffer).await - } + // Check if cards supports CMD8 (with pattern) + self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; + let cic = CIC::from(regs.respr(0).read().cardstatus()); - /// Write a block - pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { - self.sdmmc.write_blocks(&mut self.info, block_idx, blocks).await - } + if cic.pattern() != 0xAA { + return Err(Error::UnsupportedCardVersion); + } - /// Read a block - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - self.sdmmc.read_block(&mut self.info, block_idx, buffer).await - } + if cic.voltage_accepted() & 1 == 0 { + return Err(Error::UnsupportedVoltage); + } - /// Read a block - pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { - self.sdmmc.read_blocks(&mut self.info, block_idx, blocks).await - } -} + let ocr = loop { + // Signal that next command is a app command + self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 -#[derive(Clone, Copy, Debug, Default)] -/// SD Card -pub struct Card { - /// The type of this card - pub card_type: CardCapacity, - /// Operation Conditions Register - pub ocr: OCR, - /// Relative Card Address - pub rca: u16, - /// Card ID - pub cid: CID, - /// Card Specific Data - pub csd: CSD, - /// SD CARD Configuration Register - pub scr: SCR, - /// SD Status - pub status: SDStatus, -} + // 3.2-3.3V + let voltage_window = 1 << 5; + // Initialize card + match self + .sdmmc + .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) + { + // ACMD41 + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; + + if ocr.high_capacity() { + // Card is SDHC or SDXC or SDUC + self.info.card_type = CardCapacity::HighCapacity; + } else { + self.info.card_type = CardCapacity::StandardCapacity; + } + self.info.ocr = ocr; + + self.info.cid = self.sdmmc.get_cid()?.into(); + + self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; + let rca = RCA::::from(regs.respr(0).read().cardstatus()); + self.info.rca = rca.address(); + + self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); + self.sdmmc.select_card(Some(self.info.get_address()))?; + + self.info.scr = self.get_scr(cmd_block).await?; + + let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { + (BusWidth::One, 0) + } else { + (BusWidth::Four, 2) + }; + + self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; + self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; + + self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; + + // Read status + self.info.status = self.read_sd_status(cmd_block).await?; + + if freq > mhz(25) { + // Switch to SDR25 + self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; + + if self.sdmmc.signalling == Signalling::SDR25 { + // Set final clock frequency + self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; + + if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { + return Err(Error::SignalingSwitchFailed); + } + } + + // Read status after signalling change + self.read_sd_status(cmd_block).await?; + } + + Ok(()) + } -impl Card { /// Switch mode using CMD6. /// /// Attempt to set a new signalling mode. The selected @@ -261,9 +368,8 @@ impl Card { /// frequency to be > 12.5MHz. /// /// SD only. - async fn switch_signalling_mode<'a>( - &mut self, - sdmmc: &mut Sdmmc<'a>, + async fn switch_signalling_mode( + &self, cmd_block: &mut CmdBlock, signalling: Signalling, ) -> Result { @@ -281,13 +387,12 @@ impl Card { }; // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| sdmmc.on_drop()); + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, cmd_block.as_mut(), 64, 6); - sdmmc.enable_interrupts(); - sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 + let transfer = self.sdmmc.prepare_datapath_read(cmd_block.as_mut(), 64, 6); + self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - let res = sdmmc.complete_datapath_transfer(true).await; + self.sdmmc.complete_datapath_transfer(transfer, true).await?; // Host is allowed to use the new functions at least 8 // clocks after the end of the switch command @@ -297,94 +402,375 @@ impl Card { cortex_m::asm::nop(); } - match res { - Ok(_) => { - on_drop.defuse(); - sdmmc.stop_datapath(); - drop(transfer); - - // Function Selection of Function Group 1 - let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; - - match selection { - 0 => Ok(Signalling::SDR12), - 1 => Ok(Signalling::SDR25), - 2 => Ok(Signalling::SDR50), - 3 => Ok(Signalling::SDR104), - 4 => Ok(Signalling::DDR50), - _ => Err(Error::UnsupportedCardType), - } - } - Err(e) => Err(e), + on_drop.defuse(); + + // Function Selection of Function Group 1 + let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; + + match selection { + 0 => Ok(Signalling::SDR12), + 1 => Ok(Signalling::SDR25), + 2 => Ok(Signalling::SDR50), + 3 => Ok(Signalling::SDR104), + 4 => Ok(Signalling::DDR50), + _ => Err(Error::UnsupportedCardType), } } /// Reads the SCR register. /// /// SD only. - async fn get_scr<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { + async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result { // Read the 64-bit SCR register - sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 - sdmmc.cmd(common_cmd::app_cmd(self.rca), false)?; + self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 + self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; let scr = &mut cmd_block.0[..2]; // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| sdmmc.on_drop()); + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, scr, 8, 3); - sdmmc.enable_interrupts(); - sdmmc.cmd(sd_cmd::send_scr(), true)?; + let transfer = self.sdmmc.prepare_datapath_read(scr, 8, 3); + self.sdmmc.cmd(sd_cmd::send_scr(), true)?; - let res = sdmmc.complete_datapath_transfer(true).await; + self.sdmmc.complete_datapath_transfer(transfer, true).await?; - if res.is_ok() { - on_drop.defuse(); - sdmmc.stop_datapath(); - drop(transfer); + on_drop.defuse(); - unsafe { - let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); - self.scr = SCR(u64::from_be_bytes(*scr_bytes)); - } - } - res + Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) } /// Reads the SD Status (ACMD13) /// /// SD only. - async fn read_sd_status<'a>(&mut self, sdmmc: &mut Sdmmc<'a>, cmd_block: &mut CmdBlock) -> Result<(), Error> { - let rca = self.rca; + async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result { + let rca = self.info.rca; - sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 - sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP + self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 + self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP let status = cmd_block; // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| sdmmc.on_drop()); + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); + + let transfer = self.sdmmc.prepare_datapath_read(status.as_mut(), 64, 6); + self.sdmmc.cmd(sd_cmd::sd_status(), true)?; - let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, status.as_mut(), 64, 6); - sdmmc.enable_interrupts(); - sdmmc.cmd(sd_cmd::sd_status(), true)?; + self.sdmmc.complete_datapath_transfer(transfer, true).await?; - let res = sdmmc.complete_datapath_transfer(true).await; + on_drop.defuse(); + + for byte in status.iter_mut() { + *byte = u32::from_be(*byte); + } + + Ok(status.0.into()) + } +} + +/// Emmc storage device +impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { + /// Create a new EMMC card + pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { + let mut s = Self { + info: Emmc::default(), + sdmmc, + }; + + s.acquire(cmd_block, freq).await?; + + Ok(s) + } - if res.is_ok() { - on_drop.defuse(); - sdmmc.stop_datapath(); - drop(transfer); + async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let regs = self.sdmmc.info.regs; - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); + let bus_width = self.sdmmc.bus_width(); + + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + self.sdmmc.init_idle()?; + + let ocr = loop { + let high_voltage = 0b0 << 7; + let access_mode = 0b10 << 29; + let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; + // Initialize card + match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; + + self.info.capacity = if ocr.access_mode() == 0b10 { + // Card is SDHC or SDXC or SDUC + CardCapacity::HighCapacity + } else { + CardCapacity::StandardCapacity + }; + self.info.ocr = ocr; + + self.info.cid = self.sdmmc.get_cid()?.into(); + + self.info.rca = 1u16.into(); + self.sdmmc + .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; + + self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); + self.sdmmc.select_card(Some(self.info.get_address()))?; + + let (widbus, _) = bus_width_vals(bus_width); + + // Write bus width to ExtCSD byte 183 + self.sdmmc.cmd( + emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), + false, + )?; + + // Wait for ready after R1b response + loop { + let status = self.sdmmc.read_status(&self.info)?; + + if status.ready_for_data() { + break; } - self.status = status.0.into(); } - res + + self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; + self.info.ext_csd = self.read_ext_csd().await?; + + Ok(()) + } + + /// Gets the EXT_CSD register. + /// + /// eMMC only. + async fn read_ext_csd(&self) -> Result { + // Note: cmd_block can't be used because ExtCSD is too long to fit. + let mut data_block = DataBlock([0u8; 512]); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; + + self.sdmmc.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 + + let transfer = self.sdmmc.prepare_datapath_read(buffer, 512, 9); + self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + on_drop.defuse(); + + Ok(unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into()) + } +} + +/// Card or Emmc storage device +impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { + /// Write a block + pub fn card(&self) -> A { + self.info.clone() + } + + /// Read a data block. + #[inline] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let card_capacity = self.info.get_capacity(); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card_capacity { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); + + let transfer = self.sdmmc.prepare_datapath_read(buffer, 512, 9); + self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + on_drop.defuse(); + + Ok(()) + } + + /// Read multiple data blocks. + #[inline] + pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let card_capacity = self.info.get_capacity(); + + // NOTE(unsafe) reinterpret buffer as &mut [u32] + let buffer = unsafe { + let ptr = blocks.as_mut_ptr() as *mut u32; + let len = blocks.len() * 128; + core::slice::from_raw_parts_mut(ptr, len) + }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card_capacity { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); + let transfer = self.sdmmc.prepare_datapath_read(buffer, 512 * blocks.len() as u32, 9); + self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + + self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 + self.sdmmc.clear_interrupt_flags(); + + on_drop.defuse(); + + Ok(()) + } + + /// Write a data block. + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> + where + CardStatus: From, + { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; + + // Always read 1 block of 512 bytes + // cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match self.info.get_capacity() { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); + + // sdmmc_v1 uses different cmd/dma order than v2, but only for writes + #[cfg(sdmmc_v1)] + self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; + + let transfer = self.sdmmc.prepare_datapath_write(buffer, 512, 9); + + #[cfg(sdmmc_v2)] + self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + on_drop.defuse(); + + // TODO: Make this configurable + let mut timeout: u32 = 0x00FF_FFFF; + + while timeout > 0 { + let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); + if ready_for_data { + return Ok(()); + } + timeout -= 1; + } + + Err(Error::SoftwareTimeout) + } + + /// Write multiple data blocks. + pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> + where + CardStatus: From, + { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + // NOTE(unsafe) reinterpret buffer as &[u32] + let buffer = unsafe { + let ptr = blocks.as_ptr() as *const u32; + let len = blocks.len() * 128; + core::slice::from_raw_parts(ptr, len) + }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match self.info.get_capacity() { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + + self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let block_count = blocks.len(); + + let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); + + #[cfg(sdmmc_v1)] + self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 + + // Setup write command + let transfer = self.sdmmc.prepare_datapath_write(buffer, 512 * block_count as u32, 9); + + #[cfg(sdmmc_v2)] + self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + + self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 + self.sdmmc.clear_interrupt_flags(); + + on_drop.defuse(); + + // TODO: Make this configurable + let mut timeout: u32 = 0x00FF_FFFF; + + while timeout > 0 { + let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); + + if ready_for_data { + return Ok(()); + } + timeout -= 1; + } + Err(Error::SoftwareTimeout) } } +#[derive(Clone, Copy, Debug, Default)] +/// SD Card +pub struct Card { + /// The type of this card + pub card_type: CardCapacity, + /// Operation Conditions Register + pub ocr: OCR, + /// Relative Card Address + pub rca: u16, + /// Card ID + pub cid: CID, + /// Card Specific Data + pub csd: CSD, + /// SD CARD Configuration Register + pub scr: SCR, + /// SD Status + pub status: SDStatus, +} + impl Addressable for Card { type Ext = SD; @@ -421,40 +807,6 @@ pub struct Emmc { pub ext_csd: ExtCSD, } -impl Emmc { - /// Gets the EXT_CSD register. - /// - /// eMMC only. - async fn read_ext_csd<'a>(&mut self, sdmmc: &mut Sdmmc<'a>) -> Result<(), Error> { - // Note: cmd_block can't be used because ExtCSD is too long to fit. - let mut data_block = DataBlock([0u8; 512]); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; - - sdmmc.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 - - let transfer = sdmmc.prepare_datapath_read(&sdmmc.config, buffer, 512, 9); - sdmmc.enable_interrupts(); - sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| sdmmc.on_drop()); - - let res = sdmmc.complete_datapath_transfer(true).await; - - if res.is_ok() { - on_drop.defuse(); - sdmmc.stop_datapath(); - drop(transfer); - - self.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); - } - - res - } -} - impl Addressable for Emmc { type Ext = EMMC; @@ -516,6 +868,15 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { Ok((false, clk_div, clk_f)) } +fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) { + match bus_width { + BusWidth::One => (0, 1u32), + BusWidth::Four => (1, 4u32), + BusWidth::Eight => (2, 8u32), + _ => panic!("Invalid Bus Width"), + } +} + /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to /// `sdmmc_ck` in Hertz. /// @@ -580,25 +941,6 @@ impl Default for Config { } } -/// Peripheral that can be operated over SDMMC -#[derive(Clone, Copy, Debug)] -pub enum SdmmcPeripheral { - /// SD Card - SdCard(Card), - /// eMMC memory - Emmc(Emmc), -} - -impl SdmmcPeripheral { - /// Get this peripheral's address on the SDMMC bus - fn get_address(&self) -> u16 { - match self { - Self::SdCard(c) => c.rca, - Self::Emmc(e) => e.rca, - } - } -} - /// Sdmmc device pub struct Sdmmc<'d> { info: &'static Info, @@ -989,17 +1331,19 @@ impl<'d> Sdmmc<'d> { while self.data_active() || self.cmd_active() {} } + fn bus_width(&self) -> BusWidth { + match (self.d3.is_some(), self.d7.is_some()) { + (true, true) => BusWidth::Eight, + (true, false) => BusWidth::Four, + _ => BusWidth::One, + } + } + /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned #[allow(unused_variables)] - fn prepare_datapath_read<'a>( - &'a self, - config: &Config, - buffer: &'a mut [u32], - length_bytes: u32, - block_size: u8, - ) -> Transfer<'a> { + fn prepare_datapath_read<'a>(&'a self, buffer: &'a mut [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); let regs = self.info.regs; @@ -1034,6 +1378,8 @@ impl<'d> Sdmmc<'d> { } }); + self.enable_interrupts(); + transfer } @@ -1075,6 +1421,8 @@ impl<'d> Sdmmc<'d> { } }); + self.enable_interrupts(); + transfer } @@ -1091,18 +1439,23 @@ impl<'d> Sdmmc<'d> { regs.idmactrlr().modify(|w| w.set_idmaen(false)); } - /// Sets the CLKDIV field in CLKCR. Updates clock field in self - fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { + fn init_idle(&mut self) -> Result<(), Error> { let regs = self.info.regs; - let width_u32 = match width { - BusWidth::One => 1u32, - BusWidth::Four => 4u32, - BusWidth::Eight => 8u32, - _ => panic!("Invalid Bus Width"), - }; + self.clkcr_set_clkdiv(SD_INIT_FREQ, BusWidth::One)?; + 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) + } + + /// Sets the CLKDIV field in CLKCR. Updates clock field in self + fn clkcr_set_clkdiv(&mut self, freq: Hertz, width: BusWidth) -> Result<(), Error> { + let regs = self.info.regs; - let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq)?; + let (widbus, width_u32) = bus_width_vals(width); + let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq.0)?; // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 // Section 55.5.8 @@ -1110,29 +1463,34 @@ impl<'d> Sdmmc<'d> { assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32); self.clock = new_clock; - // CPSMACT and DPSMACT must be 0 to set CLKDIV + // CPSMACT and DPSMACT must be 0 to set CLKDIV or WIDBUS self.wait_idle(); regs.clkcr().modify(|w| { w.set_clkdiv(clkdiv); #[cfg(sdmmc_v1)] w.set_bypass(_bypass); + w.set_widbus(widbus); }); Ok(()) } + fn get_cid(&self) -> Result { + self.cmd(common_cmd::all_send_cid(), false) // CMD2 + } + + fn get_csd(&self, address: u16) -> Result { + self.cmd(common_cmd::send_csd(address), false) + } + /// Query the card status (CMD13, returns R1) fn read_status(&self, card: &A) -> Result, Error> where CardStatus: From, { - let regs = self.info.regs; let rca = card.get_address(); - self.cmd(common_cmd::card_status(rca, false), false)?; // CMD13 - - let r1 = regs.respr(0).read().cardstatus(); - Ok(r1.into()) + Ok(self.cmd(common_cmd::card_status(rca, false), false)?.into()) // CMD13 } /// Select one card and place it into the _Tranfer State_ @@ -1143,11 +1501,17 @@ impl<'d> Sdmmc<'d> { // Determine Relative Card Address (RCA) of given card let rca = rca.unwrap_or(0); - let r = self.cmd(common_cmd::select_card(rca), false); - match (r, rca) { - (Err(Error::Timeout), 0) => Ok(()), - _ => r, + let resp = self.cmd(common_cmd::select_card(rca), false); + + if let Err(Error::Timeout) = resp + && rca == 0 + { + return Ok(()); } + + resp?; + + Ok(()) } /// Clear flags in interrupt clear register @@ -1186,7 +1550,7 @@ impl<'d> Sdmmc<'d> { /// Send command to card #[allow(unused_variables)] - fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + fn cmd(&self, cmd: Cmd, data: bool) -> Result { let regs = self.info.regs; self.clear_interrupt_flags(); @@ -1233,7 +1597,20 @@ impl<'d> Sdmmc<'d> { } else if status.ccrcfail() { return Err(Error::Crc); } - Ok(()) + + Ok(match R::LENGTH { + ResponseLen::Zero => U128(0u128), + ResponseLen::R48 => U128(self.info.regs.respr(0).read().cardstatus() as u128), + ResponseLen::R136 => { + let cid0 = self.info.regs.respr(0).read().cardstatus() as u128; + let cid1 = self.info.regs.respr(1).read().cardstatus() as u128; + let cid2 = self.info.regs.respr(2).read().cardstatus() as u128; + let cid3 = self.info.regs.respr(3).read().cardstatus() as u128; + + U128((cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3)) + } + } + .into()) } fn on_drop(&self) { @@ -1272,7 +1649,7 @@ impl<'d> Sdmmc<'d> { /// Wait for a previously started datapath transfer to complete from an interrupt. #[inline] #[allow(unused)] - async fn complete_datapath_transfer(&self, block: bool) -> Result<(), Error> { + async fn complete_datapath_transfer(&self, transfer: Transfer<'_>, block: bool) -> Result<(), Error> { let res = poll_fn(|cx| { // Compiler might not be sufficiently constrained here // https://github.com/embassy-rs/embassy/issues/4723 @@ -1307,498 +1684,17 @@ impl<'d> Sdmmc<'d> { .await; self.clear_interrupt_flags(); + self.stop_datapath(); - res - } - - /// Read a data block. - #[inline] - pub async fn read_block( - &mut self, - card: &mut impl Addressable, - block_idx: u32, - buffer: &mut DataBlock, - ) -> Result<(), Error> { - let _scoped_block_stop = self.info.rcc.block_stop(); - let card_capacity = card.get_capacity(); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card_capacity { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let on_drop = OnDrop::new(|| self.on_drop()); - - let transfer = self.prepare_datapath_read(&self.config, buffer, 512, 9); - self.enable_interrupts(); - self.cmd(common_cmd::read_single_block(address), true)?; - - let res = self.complete_datapath_transfer(true).await; - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - } - res - } - - /// Read multiple data blocks. - #[inline] - pub async fn read_blocks( - &mut self, - card: &mut impl Addressable, - block_idx: u32, - blocks: &mut [DataBlock], - ) -> Result<(), Error> { - let _scoped_block_stop = self.info.rcc.block_stop(); - let card_capacity = card.get_capacity(); - - // NOTE(unsafe) reinterpret buffer as &mut [u32] - let buffer = unsafe { - let ptr = blocks.as_mut_ptr() as *mut u32; - let len = blocks.len() * 128; - core::slice::from_raw_parts_mut(ptr, len) - }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card_capacity { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let on_drop = OnDrop::new(|| self.on_drop()); - - let transfer = self.prepare_datapath_read(&self.config, buffer, 512 * blocks.len() as u32, 9); - self.enable_interrupts(); - - self.cmd(common_cmd::read_multiple_blocks(address), true)?; - - let res = self.complete_datapath_transfer(false).await; - - self.cmd(common_cmd::stop_transmission(), false)?; // CMD12 - self.clear_interrupt_flags(); + drop(transfer); - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - } res } - /// Write a data block. - pub async fn write_block( - &mut self, - card: &mut A, - block_idx: u32, - buffer: &DataBlock, - ) -> Result<(), Error> - where - CardStatus: From, - { - let _scoped_block_stop = self.info.rcc.block_stop(); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - - // Always read 1 block of 512 bytes - // cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card.get_capacity() { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let on_drop = OnDrop::new(|| self.on_drop()); - - // sdmmc_v1 uses different cmd/dma order than v2, but only for writes - #[cfg(sdmmc_v1)] - self.cmd(common_cmd::write_single_block(address), true)?; - - let transfer = self.prepare_datapath_write(buffer, 512, 9); - self.enable_interrupts(); - - #[cfg(sdmmc_v2)] - self.cmd(common_cmd::write_single_block(address), true)?; - - let res = self.complete_datapath_transfer(true).await; - - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - - // TODO: Make this configurable - let mut timeout: u32 = 0x00FF_FFFF; - - while timeout > 0 { - let ready_for_data = self.read_status(card)?.ready_for_data(); - if ready_for_data { - return Ok(()); - } - timeout -= 1; - } - Err(Error::SoftwareTimeout) - } - Err(e) => Err(e), - } - } - - /// Write multiple data blocks. - pub async fn write_blocks( - &mut self, - card: &mut A, - block_idx: u32, - blocks: &[DataBlock], - ) -> Result<(), Error> - where - CardStatus: From, - { - let _scoped_block_stop = self.info.rcc.block_stop(); - - // NOTE(unsafe) reinterpret buffer as &[u32] - let buffer = unsafe { - let ptr = blocks.as_ptr() as *const u32; - let len = blocks.len() * 128; - core::slice::from_raw_parts(ptr, len) - }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card.get_capacity() { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - - self.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let block_count = blocks.len(); - - let on_drop = OnDrop::new(|| self.on_drop()); - - #[cfg(sdmmc_v1)] - self.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 - - // Setup write command - let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); - self.enable_interrupts(); - - #[cfg(sdmmc_v2)] - self.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 - - let res = self.complete_datapath_transfer(false).await; - - self.cmd(common_cmd::stop_transmission(), false)?; // CMD12 - self.clear_interrupt_flags(); - - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); - drop(transfer); - - // TODO: Make this configurable - let mut timeout: u32 = 0x00FF_FFFF; - - while timeout > 0 { - let ready_for_data = self.read_status(card)?.ready_for_data(); - - if ready_for_data { - return Ok(()); - } - timeout -= 1; - } - Err(Error::SoftwareTimeout) - } - Err(e) => Err(e), - } - } - /// Get the current SDMMC bus clock pub fn clock(&self) -> Hertz { self.clock } - - async fn init_internal( - &mut self, - cmd_block: &mut CmdBlock, - freq: Hertz, - card: &mut SdmmcPeripheral, - ) -> Result<(), Error> { - let _scoped_block_stop = self.info.rcc.block_stop(); - - let regs = self.info.regs; - - let bus_width = match (self.d3.is_some(), self.d7.is_some()) { - (true, true) => { - if matches!(card, SdmmcPeripheral::SdCard(_)) { - return Err(Error::BusWidth); - } - BusWidth::Eight - } - (true, false) => BusWidth::Four, - _ => BusWidth::One, - }; - - // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be no more than 400 kHz. - let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(self.ker_clk, SD_INIT_FREQ.0)); - self.clock = init_clock; - - // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); - - regs.clkcr().modify(|w| { - w.set_widbus(0); - w.set_clkdiv(clkdiv); - #[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)?; - - match card { - SdmmcPeripheral::SdCard(card) => { - // Check if cards supports CMD8 (with pattern) - self.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; - let cic = CIC::from(regs.respr(0).read().cardstatus()); - - if cic.pattern() != 0xAA { - return Err(Error::UnsupportedCardVersion); - } - - if cic.voltage_accepted() & 1 == 0 { - return Err(Error::UnsupportedVoltage); - } - - let ocr = loop { - // Signal that next command is a app command - self.cmd(common_cmd::app_cmd(0), false)?; // CMD55 - - // 3.2-3.3V - let voltage_window = 1 << 5; - // Initialize card - match self.cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { - // ACMD41 - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), - } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; - - if ocr.high_capacity() { - // Card is SDHC or SDXC or SDUC - card.card_type = CardCapacity::HighCapacity; - } else { - card.card_type = CardCapacity::StandardCapacity; - } - card.ocr = ocr; - } - SdmmcPeripheral::Emmc(emmc) => { - let ocr = loop { - let high_voltage = 0b0 << 7; - let access_mode = 0b10 << 29; - let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; - // Initialize card - match self.cmd(emmc_cmd::send_op_cond(op_cond), false) { - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), - } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; - - emmc.capacity = if ocr.access_mode() == 0b10 { - // Card is SDHC or SDXC or SDUC - CardCapacity::HighCapacity - } else { - CardCapacity::StandardCapacity - }; - emmc.ocr = ocr; - } - } - - self.cmd(common_cmd::all_send_cid(), false)?; // CMD2 - let cid0 = regs.respr(0).read().cardstatus() as u128; - let cid1 = regs.respr(1).read().cardstatus() as u128; - let cid2 = regs.respr(2).read().cardstatus() as u128; - let cid3 = regs.respr(3).read().cardstatus() as u128; - let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); - - match card { - SdmmcPeripheral::SdCard(card) => { - card.cid = cid.into(); - - self.cmd(sd_cmd::send_relative_address(), false)?; - let rca = RCA::::from(regs.respr(0).read().cardstatus()); - card.rca = rca.address(); - } - SdmmcPeripheral::Emmc(emmc) => { - emmc.cid = cid.into(); - - emmc.rca = 1u16.into(); - self.cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; - } - } - - self.cmd(common_cmd::send_csd(card.get_address()), false)?; - let csd0 = regs.respr(0).read().cardstatus() as u128; - let csd1 = regs.respr(1).read().cardstatus() as u128; - let csd2 = regs.respr(2).read().cardstatus() as u128; - let csd3 = regs.respr(3).read().cardstatus() as u128; - let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); - - self.select_card(Some(card.get_address()))?; - - let bus_width = match card { - SdmmcPeripheral::SdCard(card) => { - card.csd = csd.into(); - - card.get_scr(self, cmd_block).await?; - - if !card.scr.bus_width_four() { - BusWidth::One - } else { - BusWidth::Four - } - } - SdmmcPeripheral::Emmc(emmc) => { - emmc.csd = csd.into(); - - bus_width - } - }; - - // Set bus width - let widbus = match bus_width { - BusWidth::Eight => 2, - BusWidth::Four => 1, - BusWidth::One => 0, - _ => unreachable!(), - }; - - match card { - SdmmcPeripheral::SdCard(card) => { - let acmd_arg = match bus_width { - BusWidth::Four if card.scr.bus_width_four() => 2, - _ => 0, - }; - self.cmd(common_cmd::app_cmd(card.rca), false)?; - self.cmd(sd_cmd::cmd6(acmd_arg), false)?; - } - SdmmcPeripheral::Emmc(emmc) => { - // Write bus width to ExtCSD byte 183 - self.cmd( - emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), - false, - )?; - - // Wait for ready after R1b response - loop { - let status = self.read_status(emmc)?; - - if status.ready_for_data() { - break; - } - } - } - } - - // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); - - regs.clkcr().modify(|w| w.set_widbus(widbus)); - - // Set Clock - if freq.0 <= 25_000_000 { - // Final clock frequency - self.clkcr_set_clkdiv(freq.0, bus_width)?; - } else { - // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, bus_width)?; - } - - match card { - SdmmcPeripheral::SdCard(card) => { - // Read status - card.read_sd_status(self, cmd_block).await?; - - if freq.0 > 25_000_000 { - // Switch to SDR25 - self.signalling = card.switch_signalling_mode(self, cmd_block, Signalling::SDR25).await?; - - if self.signalling == Signalling::SDR25 { - // Set final clock frequency - self.clkcr_set_clkdiv(freq.0, bus_width)?; - - if self.read_status(card)?.state() != CurrentState::Transfer { - return Err(Error::SignalingSwitchFailed); - } - } - } - - // Read status after signalling change - card.read_sd_status(self, cmd_block).await?; - } - SdmmcPeripheral::Emmc(emmc) => { - emmc.read_ext_csd(self).await?; - } - } - - Ok(()) - } - - /// Initializes card (if present) and sets the bus at the specified frequency. - /// - /// SD only. - async fn init_sd_card(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let mut card = SdmmcPeripheral::SdCard(Card::default()); - self.init_internal(cmd_block, freq, &mut card).await?; - - let card = match card { - SdmmcPeripheral::SdCard(card) => card, - _ => unreachable!(), - }; - - Ok(card) - } - - /// Initializes eMMC and sets the bus at the specified frequency. - /// - /// eMMC only. - async fn init_emmc(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let mut card = SdmmcPeripheral::Emmc(Emmc::default()); - self.init_internal(cmd_block, freq, &mut card).await?; - - let card = match card { - SdmmcPeripheral::Emmc(card) => card, - _ => unreachable!(), - }; - - Ok(card) - } } impl<'d> Drop for Sdmmc<'d> { diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 454d0dd57..d98b93a15 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -56,15 +56,59 @@ async fn main(_spawner: Spawner) { let mut cmd_block = CmdBlock::new(); - let mut storage = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(24)) - .await - .unwrap(); + let mut storage = loop { + if let Ok(storage) = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(24)).await { + break storage; + } + }; let card = storage.card(); info!("Card: {:#?}", Debug2Format(&card)); info!("Clock: {}", storage.sdmmc.clock()); + // card_type: HighCapacity, + // ocr: OCR: Operation Conditions Register { + // Voltage Window (mV): (2700, 3600), + // S18A (UHS-I only): true, + // Over 2TB flag (SDUC only): false, + // UHS-II Card: false, + // Card Capacity Status (CSS): \"SDHC/SDXC/SDUC\", + // Busy: false }, + // rca: 43690, + // cid: CID: Card Identification { Manufacturer ID: 3, + // OEM ID: \"SD\", + // Product Name: \"SL08G\", + // Product Revision: 128, + // Product Serial Number: 701445767, + // Manufacturing Date: (9, + // 2015) }, + // csd: CSD: Card Specific Data { Transfer Rate: 50, + // Block Count: 15523840, + // Card Size (bytes): 7948206080, + // Read I (@min VDD): 100 mA, + // Write I (@min VDD): 10 mA, + // Read I (@max VDD): 5 mA, + // Write I (@max VDD): 45 mA, + // Erase Size (Blocks): 1 }, + // scr: SCR: SD CARD Configuration Register { Version: Unknown, + // 1-bit width: false, + // 4-bit width: true }, + // status: SD Status { Bus Width: One, + // Secured Mode: false, + // SD Memory Card Type: 0, + // Protected Area Size (B): 0, + // Speed Class: 0, + // Video Speed Class: 0, + // Application Performance Class: 0, + // Move Performance (MB/s): 0, + // AU Size: 0, + // Erase Size (units of AU): 0, + // Erase Timeout (s): 0, + // Discard Support: false } } + + defmt::assert!(card.scr.bus_width_four()); + info!("writing pattern1..."); storage.write_block(block_idx, &pattern1).await.unwrap(); -- cgit From 80a8859b2f4ad215ac04e4c55649f6f47191b035 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 10 Dec 2025 23:54:48 +0100 Subject: ci: Remove useless `-n embassy`. it's already specified in the kubeconfig. --- .github/ci/book.sh | 2 +- .github/ci/doc.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ci/book.sh b/.github/ci/book.sh index 6c300bf09..a39f0dac7 100755 --- a/.github/ci/book.sh +++ b/.github/ci/book.sh @@ -9,7 +9,7 @@ set -euxo pipefail make -C docs export KUBECONFIG=/ci/secrets/kubeconfig.yml -POD=$(kubectl -n embassy get po -l app=website -o jsonpath={.items[0].metadata.name}) +POD=$(kubectl get po -l app=website -o jsonpath={.items[0].metadata.name}) mkdir -p build mv docs/book build/book diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 5a0032d9d..85eed9f80 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -17,6 +17,6 @@ cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked cargo embassy-devtool doc -o webroot export KUBECONFIG=/ci/secrets/kubeconfig.yml -POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) +POD=$(kubectl get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data kubectl cp webroot/static $POD:/data -- cgit From 86271ea39e841bfa7f8d74defc4aaa07a680be13 Mon Sep 17 00:00:00 2001 From: Lambert Sartory Date: Thu, 11 Dec 2025 00:04:37 +0100 Subject: Enable STM32N6 DMA and I2C clock sources --- embassy-stm32/src/dma/gpdma/mod.rs | 1 - embassy-stm32/src/dma/mod.rs | 4 ---- embassy-stm32/src/rcc/n6.rs | 20 ++++++++++++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 51c107cb4..afb18ec1a 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs @@ -137,7 +137,6 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: c impl AnyChannel { /// Safety: Must be called with a matching set of parameters for a valid dma channel - #[cfg(not(stm32n6))] pub(crate) unsafe fn on_irq(&self) { let info = self.info(); #[cfg(feature = "_dual-core")] diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 90feab167..05d9c2e51 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -48,11 +48,9 @@ pub type Request = u8; pub type Request = (); pub(crate) trait SealedChannel: StoppablePeripheral { - #[cfg(not(stm32n6))] fn id(&self) -> u8; } -#[cfg(not(stm32n6))] pub(crate) trait ChannelInterrupt { #[cfg_attr(not(feature = "rt"), allow(unused))] unsafe fn on_irq(); @@ -62,7 +60,6 @@ pub(crate) trait ChannelInterrupt { #[allow(private_bounds)] pub trait Channel: SealedChannel + PeripheralType + Into + 'static {} -#[cfg(not(stm32n6))] macro_rules! dma_channel_impl { ($channel_peri:ident, $index:expr, $stop_mode:ident) => { impl crate::rcc::StoppablePeripheral for crate::peripherals::$channel_peri { @@ -125,7 +122,6 @@ impl StoppablePeripheral for AnyChannel { } impl SealedChannel for AnyChannel { - #[cfg(not(stm32n6))] fn id(&self) -> u8 { self.id } diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs index 866851bbd..178ec57d4 100644 --- a/embassy-stm32/src/rcc/n6.rs +++ b/embassy-stm32/src/rcc/n6.rs @@ -1003,6 +1003,24 @@ pub(crate) unsafe fn init(config: Config) { p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); } + // TODO: ugly workaround for DMA accesses until RIF is properly implemented + debug!("deactivating RIF"); + const RISAF3_BASE_NS: *mut u32 = stm32_metapac::RNG.wrapping_byte_offset(0x8000) as _; // AHB3PERIPH_BASE_NS + 0x8000UL + const RISAF3_REG0_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x40); + const RISAF3_REG0_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x48); + const RISAF3_REG0_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x4C); + const RISAF3_REG1_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x80); + const RISAF3_REG1_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x88); + const RISAF3_REG1_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x8C); + unsafe { + *RISAF3_REG0_CIDCFGR = 0x000F000F; /* RW for everyone */ + *RISAF3_REG0_ENDR = 0xFFFFFFFF; /* all-encompassing */ + *RISAF3_REG0_CFGR = 0x00000101; /* enabled, secure, unprivileged for everyone */ + *RISAF3_REG1_CIDCFGR = 0x00FF00FF; /* RW for everyone */ + *RISAF3_REG1_ENDR = 0xFFFFFFFF; /* all-encompassing */ + *RISAF3_REG1_CFGR = 0x00000001; /* enabled, non-secure, unprivileged*/ + } + debug!("setting power supply config"); power_supply_config(config.supply_config); @@ -1039,7 +1057,9 @@ pub(crate) unsafe fn init(config: Config) { i2s_ckin: None, ic8: None, ic9: None, + ic10: None, ic14: None, + ic15: None, ic17: None, ic20: None, ); -- cgit From 286571a17bcb34c271eb64c8e9aca736599309e1 Mon Sep 17 00:00:00 2001 From: Lambert Sartory Date: Thu, 11 Dec 2025 00:05:24 +0100 Subject: Add more STM32N6 examples --- examples/stm32n6/Cargo.toml | 2 ++ examples/stm32n6/src/bin/crc.rs | 31 ++++++++++++++++ examples/stm32n6/src/bin/hash.rs | 78 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 examples/stm32n6/src/bin/crc.rs create mode 100644 examples/stm32n6/src/bin/hash.rs diff --git a/examples/stm32n6/Cargo.toml b/examples/stm32n6/Cargo.toml index 5ed28eed1..5ad5b97ce 100644 --- a/examples/stm32n6/Cargo.toml +++ b/examples/stm32n6/Cargo.toml @@ -32,6 +32,8 @@ micromath = "2.0.0" stm32-fmc = "0.3.0" embedded-storage = "0.3.1" static_cell = "2" +hmac = "0.12.1" +sha2 = { version = "0.10.9", default-features = false } # cargo build/run diff --git a/examples/stm32n6/src/bin/crc.rs b/examples/stm32n6/src/bin/crc.rs new file mode 100644 index 000000000..d1b545d5b --- /dev/null +++ b/examples/stm32n6/src/bin/crc.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0 + let mut crc = Crc::new( + p.CRC, + unwrap!(Config::new( + InputReverseConfig::Byte, + true, + PolySize::Width32, + 0xFFFFFFFF, + 0x04C11DB7 + )), + ); + + let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF; + + defmt::assert_eq!(output, 0x33F0E26B); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32n6/src/bin/hash.rs b/examples/stm32n6/src/bin/hash.rs new file mode 100644 index 000000000..9f248318f --- /dev/null +++ b/examples/stm32n6/src/bin/hash.rs @@ -0,0 +1,78 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::hash::*; +use embassy_stm32::{Config, bind_interrupts, hash, peripherals}; +use embassy_time::Instant; +use hmac::{Hmac, Mac}; +use sha2::{Digest, Sha256}; +use {defmt_rtt as _, panic_probe as _}; + +type HmacSha256 = Hmac; + +bind_interrupts!(struct Irqs { + HASH => hash::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let config = Config::default(); + let p = embassy_stm32::init(config); + + let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; + let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; + + let mut hw_hasher = Hash::new_blocking(p.HASH, Irqs); + + let hw_start_time = Instant::now(); + + // Compute a digest in hardware. + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None); + hw_hasher.update_blocking(&mut context, test_1); + hw_hasher.update_blocking(&mut context, test_2); + let mut hw_digest: [u8; 32] = [0; 32]; + hw_hasher.finish_blocking(context, &mut hw_digest); + + let hw_end_time = Instant::now(); + let hw_execution_time = hw_end_time - hw_start_time; + + let sw_start_time = Instant::now(); + + // Compute a digest in software. + let mut sw_hasher = Sha256::new(); + sw_hasher.update(test_1); + sw_hasher.update(test_2); + let sw_digest = sw_hasher.finalize(); + + let sw_end_time = Instant::now(); + let sw_execution_time = sw_end_time - sw_start_time; + + info!("Hardware Digest: {:?}", hw_digest); + info!("Software Digest: {:?}", sw_digest[..]); + info!("Hardware Execution Time: {:?}", hw_execution_time); + info!("Software Execution Time: {:?}", sw_execution_time); + assert_eq!(hw_digest, sw_digest[..]); + + let hmac_key: [u8; 64] = [0x55; 64]; + + // Compute HMAC in hardware. + let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key)); + hw_hasher.update_blocking(&mut sha256hmac_context, test_1); + hw_hasher.update_blocking(&mut sha256hmac_context, test_2); + let mut hw_hmac: [u8; 32] = [0; 32]; + hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac); + + // Compute HMAC in software. + let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap(); + sw_mac.update(test_1); + sw_mac.update(test_2); + let sw_hmac = sw_mac.finalize().into_bytes(); + + info!("Hardware HMAC: {:?}", hw_hmac); + info!("Software HMAC: {:?}", sw_hmac[..]); + assert_eq!(hw_hmac, sw_hmac[..]); + + loop {} +} -- cgit From 783e720b374edc84b9bd8de853e34e6b39fe8ca4 Mon Sep 17 00:00:00 2001 From: Lambert Sartory Date: Thu, 11 Dec 2025 00:49:38 +0100 Subject: Update 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 e10409112..7989fc5d7 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -200,11 +200,11 @@ aligned = "0.4.1" heapless = "0.9.1" #stm32-metapac = { version = "18" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74" } [build-dependencies] #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74", default-features = false, features = ["metadata"] } proc-macro2 = "1.0.36" quote = "1.0.15" -- cgit From 48100a2e8d15364f6243d3db0a649e5c90c9ffe7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 Dec 2025 08:00:24 -0600 Subject: sdmmc: factor out sd and add sdio mod --- embassy-stm32/src/sdmmc/mod.rs | 823 +++++--------------------------------- embassy-stm32/src/sdmmc/sd.rs | 693 ++++++++++++++++++++++++++++++++ embassy-stm32/src/sdmmc/sdio.rs | 177 ++++++++ examples/stm32f4/src/bin/sdmmc.rs | 5 +- examples/stm32f7/src/bin/sdmmc.rs | 3 +- examples/stm32h7/src/bin/sdmmc.rs | 3 +- tests/stm32/src/bin/sdmmc.rs | 11 +- 7 files changed, 993 insertions(+), 722 deletions(-) create mode 100644 embassy-stm32/src/sdmmc/sd.rs create mode 100644 embassy-stm32/src/sdmmc/sdio.rs diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 0d5260016..12086cd3a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -4,18 +4,15 @@ use core::default::Default; use core::future::poll_fn; use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; use core::slice; use core::task::Poll; -use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; +use sdio_host::Cmd; use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; -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::sd::{BusWidth, CardStatus}; use sdio_host::sd_cmd::{R6, R7}; -use sdio_host::{Cmd, emmc_cmd, sd_cmd}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; @@ -25,9 +22,16 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; -use crate::time::{Hertz, mhz}; +use crate::sdmmc::sd::Addressable; +use crate::time::Hertz; use crate::{interrupt, peripherals}; +/// Module for SD and EMMC cards +pub mod sd; + +/// Module for SDIO interface +pub mod sdio; + /// Interrupt handler. pub struct InterruptHandler { _phantom: PhantomData, @@ -135,59 +139,14 @@ impl Default for Signalling { } } -/// Aligned data block for SDMMC transfers. -/// -/// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. -#[repr(align(4))] -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DataBlock(pub [u8; 512]); - -impl Deref for DataBlock { - type Target = [u8; 512]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for DataBlock { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { +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) } } -/// Command Block buffer for SDMMC command transfers. -/// -/// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CmdBlock(pub [u32; 16]); - -impl CmdBlock { - /// Creates a new instance of CmdBlock - pub const fn new() -> Self { - Self([0u32; 16]) - } -} - -impl Deref for CmdBlock { - type Target = [u32; 16]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for CmdBlock { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } +const fn slice8_ref(x: &[u32]) -> &[u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts(x.as_ptr() as _, len) } } /// Errors @@ -222,610 +181,6 @@ pub enum Error { StBitErr, } -/// Represents either an SD or EMMC card -pub trait Addressable: Sized + Clone { - /// Associated type - type Ext; - - /// Get this peripheral's address on the SDMMC bus - fn get_address(&self) -> u16; - - /// Is this a standard or high capacity peripheral? - fn get_capacity(&self) -> CardCapacity; - - /// Size in bytes - fn size(&self) -> u64; -} - -/// Storage Device -pub struct StorageDevice<'a, 'b, T: Addressable> { - info: T, - /// Inner member - pub sdmmc: &'a mut Sdmmc<'b>, -} - -/// Card Storage Device -impl<'a, 'b> StorageDevice<'a, 'b, Card> { - /// Create a new SD card - pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let mut s = Self { - info: Card::default(), - sdmmc, - }; - - s.acquire(cmd_block, freq).await?; - - Ok(s) - } - - /// Initializes the card into a known state (or at least tries to). - pub async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { - let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - let regs = self.sdmmc.info.regs; - - let _bus_width = match self.sdmmc.bus_width() { - BusWidth::Eight => return Err(Error::BusWidth), - bus_width => bus_width, - }; - - // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be no more than 400 kHz. - self.sdmmc.init_idle()?; - - // Check if cards supports CMD8 (with pattern) - self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; - let cic = CIC::from(regs.respr(0).read().cardstatus()); - - if cic.pattern() != 0xAA { - return Err(Error::UnsupportedCardVersion); - } - - if cic.voltage_accepted() & 1 == 0 { - return Err(Error::UnsupportedVoltage); - } - - let ocr = loop { - // Signal that next command is a app command - self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 - - // 3.2-3.3V - let voltage_window = 1 << 5; - // Initialize card - match self - .sdmmc - .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) - { - // ACMD41 - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), - } - - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; - - if ocr.high_capacity() { - // Card is SDHC or SDXC or SDUC - self.info.card_type = CardCapacity::HighCapacity; - } else { - self.info.card_type = CardCapacity::StandardCapacity; - } - self.info.ocr = ocr; - - self.info.cid = self.sdmmc.get_cid()?.into(); - - self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; - let rca = RCA::::from(regs.respr(0).read().cardstatus()); - self.info.rca = rca.address(); - - self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); - self.sdmmc.select_card(Some(self.info.get_address()))?; - - self.info.scr = self.get_scr(cmd_block).await?; - - let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { - (BusWidth::One, 0) - } else { - (BusWidth::Four, 2) - }; - - self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; - self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; - - self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; - - // Read status - self.info.status = self.read_sd_status(cmd_block).await?; - - if freq > mhz(25) { - // Switch to SDR25 - self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; - - if self.sdmmc.signalling == Signalling::SDR25 { - // Set final clock frequency - self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; - - if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { - return Err(Error::SignalingSwitchFailed); - } - } - - // Read status after signalling change - self.read_sd_status(cmd_block).await?; - } - - Ok(()) - } - - /// Switch mode using CMD6. - /// - /// Attempt to set a new signalling mode. The selected - /// signalling mode is returned. Expects the current clock - /// frequency to be > 12.5MHz. - /// - /// SD only. - async fn switch_signalling_mode( - &self, - cmd_block: &mut CmdBlock, - signalling: Signalling, - ) -> Result { - // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not - // necessary" - - let set_function = 0x8000_0000 - | match signalling { - // See PLSS v7_10 Table 4-11 - Signalling::DDR50 => 0xFF_FF04, - Signalling::SDR104 => 0xFF_1F03, - Signalling::SDR50 => 0xFF_1F02, - Signalling::SDR25 => 0xFF_FF01, - Signalling::SDR12 => 0xFF_FF00, - }; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - let transfer = self.sdmmc.prepare_datapath_read(cmd_block.as_mut(), 64, 6); - self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - - self.sdmmc.complete_datapath_transfer(transfer, true).await?; - - // Host is allowed to use the new functions at least 8 - // clocks after the end of the switch command - // transaction. We know the current clock period is < 80ns, - // so a total delay of 640ns is required here - for _ in 0..300 { - cortex_m::asm::nop(); - } - - on_drop.defuse(); - - // Function Selection of Function Group 1 - let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; - - match selection { - 0 => Ok(Signalling::SDR12), - 1 => Ok(Signalling::SDR25), - 2 => Ok(Signalling::SDR50), - 3 => Ok(Signalling::SDR104), - 4 => Ok(Signalling::DDR50), - _ => Err(Error::UnsupportedCardType), - } - } - - /// Reads the SCR register. - /// - /// SD only. - async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result { - // Read the 64-bit SCR register - self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 - self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; - - let scr = &mut cmd_block.0[..2]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - let transfer = self.sdmmc.prepare_datapath_read(scr, 8, 3); - self.sdmmc.cmd(sd_cmd::send_scr(), true)?; - - self.sdmmc.complete_datapath_transfer(transfer, true).await?; - - on_drop.defuse(); - - Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) - } - - /// Reads the SD Status (ACMD13) - /// - /// SD only. - async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result { - let rca = self.info.rca; - - self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 - self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP - - let status = cmd_block; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - let transfer = self.sdmmc.prepare_datapath_read(status.as_mut(), 64, 6); - self.sdmmc.cmd(sd_cmd::sd_status(), true)?; - - self.sdmmc.complete_datapath_transfer(transfer, true).await?; - - on_drop.defuse(); - - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); - } - - Ok(status.0.into()) - } -} - -/// Emmc storage device -impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { - /// Create a new EMMC card - pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { - let mut s = Self { - info: Emmc::default(), - sdmmc, - }; - - s.acquire(cmd_block, freq).await?; - - Ok(s) - } - - async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { - let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - let regs = self.sdmmc.info.regs; - - let bus_width = self.sdmmc.bus_width(); - - // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be no more than 400 kHz. - self.sdmmc.init_idle()?; - - let ocr = loop { - let high_voltage = 0b0 << 7; - let access_mode = 0b10 << 29; - let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; - // Initialize card - match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), - } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; - - self.info.capacity = if ocr.access_mode() == 0b10 { - // Card is SDHC or SDXC or SDUC - CardCapacity::HighCapacity - } else { - CardCapacity::StandardCapacity - }; - self.info.ocr = ocr; - - self.info.cid = self.sdmmc.get_cid()?.into(); - - self.info.rca = 1u16.into(); - self.sdmmc - .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; - - self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); - self.sdmmc.select_card(Some(self.info.get_address()))?; - - let (widbus, _) = bus_width_vals(bus_width); - - // Write bus width to ExtCSD byte 183 - self.sdmmc.cmd( - emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), - false, - )?; - - // Wait for ready after R1b response - loop { - let status = self.sdmmc.read_status(&self.info)?; - - if status.ready_for_data() { - break; - } - } - - self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; - self.info.ext_csd = self.read_ext_csd().await?; - - Ok(()) - } - - /// Gets the EXT_CSD register. - /// - /// eMMC only. - async fn read_ext_csd(&self) -> Result { - // Note: cmd_block can't be used because ExtCSD is too long to fit. - let mut data_block = DataBlock([0u8; 512]); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; - - self.sdmmc.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 - - let transfer = self.sdmmc.prepare_datapath_read(buffer, 512, 9); - self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - self.sdmmc.complete_datapath_transfer(transfer, true).await?; - - on_drop.defuse(); - - Ok(unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into()) - } -} - -/// Card or Emmc storage device -impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { - /// Write a block - pub fn card(&self) -> A { - self.info.clone() - } - - /// Read a data block. - #[inline] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - let card_capacity = self.info.get_capacity(); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card_capacity { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - let transfer = self.sdmmc.prepare_datapath_read(buffer, 512, 9); - self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; - - self.sdmmc.complete_datapath_transfer(transfer, true).await?; - - on_drop.defuse(); - - Ok(()) - } - - /// Read multiple data blocks. - #[inline] - pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { - let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - let card_capacity = self.info.get_capacity(); - - // NOTE(unsafe) reinterpret buffer as &mut [u32] - let buffer = unsafe { - let ptr = blocks.as_mut_ptr() as *mut u32; - let len = blocks.len() * 128; - core::slice::from_raw_parts_mut(ptr, len) - }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card_capacity { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - let transfer = self.sdmmc.prepare_datapath_read(buffer, 512 * blocks.len() as u32, 9); - self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; - - self.sdmmc.complete_datapath_transfer(transfer, false).await?; - - self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 - self.sdmmc.clear_interrupt_flags(); - - on_drop.defuse(); - - Ok(()) - } - - /// Write a data block. - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> - where - CardStatus: From, - { - let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - - // Always read 1 block of 512 bytes - // cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match self.info.get_capacity() { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - // sdmmc_v1 uses different cmd/dma order than v2, but only for writes - #[cfg(sdmmc_v1)] - self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; - - let transfer = self.sdmmc.prepare_datapath_write(buffer, 512, 9); - - #[cfg(sdmmc_v2)] - self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; - - self.sdmmc.complete_datapath_transfer(transfer, true).await?; - - on_drop.defuse(); - - // TODO: Make this configurable - let mut timeout: u32 = 0x00FF_FFFF; - - while timeout > 0 { - let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); - if ready_for_data { - return Ok(()); - } - timeout -= 1; - } - - Err(Error::SoftwareTimeout) - } - - /// Write multiple data blocks. - pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> - where - CardStatus: From, - { - let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); - - // NOTE(unsafe) reinterpret buffer as &[u32] - let buffer = unsafe { - let ptr = blocks.as_ptr() as *const u32; - let len = blocks.len() * 128; - core::slice::from_raw_parts(ptr, len) - }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match self.info.get_capacity() { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - - self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 - - let block_count = blocks.len(); - - let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); - - #[cfg(sdmmc_v1)] - self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 - - // Setup write command - let transfer = self.sdmmc.prepare_datapath_write(buffer, 512 * block_count as u32, 9); - - #[cfg(sdmmc_v2)] - self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 - - self.sdmmc.complete_datapath_transfer(transfer, false).await?; - - self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 - self.sdmmc.clear_interrupt_flags(); - - on_drop.defuse(); - - // TODO: Make this configurable - let mut timeout: u32 = 0x00FF_FFFF; - - while timeout > 0 { - let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); - - if ready_for_data { - return Ok(()); - } - timeout -= 1; - } - Err(Error::SoftwareTimeout) - } -} - -#[derive(Clone, Copy, Debug, Default)] -/// SD Card -pub struct Card { - /// The type of this card - pub card_type: CardCapacity, - /// Operation Conditions Register - pub ocr: OCR, - /// Relative Card Address - pub rca: u16, - /// Card ID - pub cid: CID, - /// Card Specific Data - pub csd: CSD, - /// SD CARD Configuration Register - pub scr: SCR, - /// SD Status - pub status: SDStatus, -} - -impl Addressable for Card { - type Ext = SD; - - /// Get this peripheral's address on the SDMMC bus - fn get_address(&self) -> u16 { - self.rca - } - - /// Is this a standard or high capacity peripheral? - fn get_capacity(&self) -> CardCapacity { - self.card_type - } - - /// Size in bytes - fn size(&self) -> u64 { - u64::from(self.csd.block_count()) * 512 - } -} - -#[derive(Clone, Copy, Debug, Default)] -/// eMMC storage -pub struct Emmc { - /// The capacity of this card - pub capacity: CardCapacity, - /// Operation Conditions Register - pub ocr: OCR, - /// Relative Card Address - pub rca: u16, - /// Card ID - pub cid: CID, - /// Card Specific Data - pub csd: CSD, - /// Extended Card Specific Data - pub ext_csd: ExtCSD, -} - -impl Addressable for Emmc { - type Ext = EMMC; - - /// Get this peripheral's address on the SDMMC bus - fn get_address(&self) -> u16 { - self.rca - } - - /// Is this a standard or high capacity peripheral? - fn get_capacity(&self) -> CardCapacity { - self.capacity - } - - /// Size in bytes - fn size(&self) -> u64 { - u64::from(self.ext_csd.sector_count()) * 512 - } -} - #[repr(u8)] enum PowerCtrl { Off = 0b00, @@ -877,6 +232,46 @@ fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) { } } +#[repr(u8)] +enum BlockSize { + Size1 = 0b0000, + Size2 = 0b0001, + Size4 = 0b0010, + Size8 = 0b0011, + Size16 = 0b0100, + Size32 = 0b0101, + Size64 = 0b0110, + Size128 = 0b0111, + Size256 = 0b1000, + Size512 = 0b1001, + Size1024 = 0b1010, + Size2048 = 0b1011, + Size4096 = 0b1100, + Size8192 = 0b1101, + Size16384 = 0b1110, +} + +const fn block_size(bytes: usize) -> BlockSize { + match bytes { + 1 => BlockSize::Size1, + 2 => BlockSize::Size2, + 4 => BlockSize::Size4, + 8 => BlockSize::Size8, + 16 => BlockSize::Size16, + 32 => BlockSize::Size32, + 64 => BlockSize::Size64, + 128 => BlockSize::Size128, + 256 => BlockSize::Size256, + 512 => BlockSize::Size512, + 1024 => BlockSize::Size1024, + 2048 => BlockSize::Size2048, + 4096 => BlockSize::Size4096, + 8192 => BlockSize::Size8192, + 16384 => BlockSize::Size16384, + _ => core::unreachable!(), + } +} + /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to /// `sdmmc_ck` in Hertz. /// @@ -904,6 +299,34 @@ struct Transfer<'a> { _dummy: PhantomData<&'a ()>, } +struct WrappedTransfer<'a> { + _transfer: Transfer<'a>, + sdmmc: &'a Sdmmc<'a>, + defused: bool, +} + +impl<'a> WrappedTransfer<'a> { + pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self { + Self { + _transfer, + sdmmc, + defused: false, + } + } + + pub fn defuse(&mut self) { + self.defused = true; + } +} + +impl<'a> Drop for WrappedTransfer<'a> { + fn drop(&mut self) { + if !self.defused { + self.sdmmc.on_drop(); + } + } +} + #[cfg(all(sdmmc_v1, dma))] const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, @@ -1343,15 +766,19 @@ impl<'d> Sdmmc<'d> { /// /// `buffer` must be valid for the whole transfer and word aligned #[allow(unused_variables)] - fn prepare_datapath_read<'a>(&'a self, buffer: &'a mut [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); + fn prepare_datapath_read<'a>( + &'a self, + buffer: &'a mut [u32], + block_size: BlockSize, + byte_mode: bool, + ) -> WrappedTransfer<'a> { let regs = self.info.regs; // Command AND Data state machines must be idle self.wait_idle(); self.clear_interrupt_flags(); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); + regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] @@ -1368,8 +795,12 @@ impl<'d> Sdmmc<'d> { } }; + #[cfg(sdmmc_v2)] + let byte_mode = byte_mode as u8; + regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); + w.set_dtmode(byte_mode); + w.set_dblocksize(block_size as u8); w.set_dtdir(true); #[cfg(sdmmc_v1)] { @@ -1380,21 +811,25 @@ impl<'d> Sdmmc<'d> { self.enable_interrupts(); - transfer + WrappedTransfer::new(transfer, &self) } /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - fn prepare_datapath_write<'a>(&'a self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); + fn prepare_datapath_write<'a>( + &'a self, + buffer: &'a [u32], + block_size: BlockSize, + byte_mode: bool, + ) -> WrappedTransfer<'a> { let regs = self.info.regs; // Command AND Data state machines must be idle self.wait_idle(); self.clear_interrupt_flags(); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); + regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] @@ -1411,8 +846,12 @@ impl<'d> Sdmmc<'d> { } }; + #[cfg(sdmmc_v2)] + let byte_mode = byte_mode as u8; + regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); + w.set_dtmode(byte_mode); + w.set_dblocksize(block_size as u8); w.set_dtdir(false); #[cfg(sdmmc_v1)] { @@ -1423,7 +862,7 @@ impl<'d> Sdmmc<'d> { self.enable_interrupts(); - transfer + WrappedTransfer::new(transfer, &self) } /// Stops the DMA datapath @@ -1649,7 +1088,7 @@ impl<'d> Sdmmc<'d> { /// Wait for a previously started datapath transfer to complete from an interrupt. #[inline] #[allow(unused)] - async fn complete_datapath_transfer(&self, transfer: Transfer<'_>, block: bool) -> Result<(), Error> { + async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> { let res = poll_fn(|cx| { // Compiler might not be sufficiently constrained here // https://github.com/embassy-rs/embassy/issues/4723 @@ -1686,6 +1125,7 @@ impl<'d> Sdmmc<'d> { self.clear_interrupt_flags(); self.stop_datapath(); + transfer.defuse(); drop(transfer); res @@ -1800,46 +1240,3 @@ foreach_peripheral!( } }; ); - -impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { - type Error = Error; - type Align = aligned::A4; - - async fn read( - &mut self, - block_address: u32, - buf: &mut [aligned::Aligned], - ) -> Result<(), Self::Error> { - // TODO: I think block_address needs to be adjusted by the partition start offset - if buf.len() == 1 { - let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; - self.read_block(block_address, block).await?; - } else { - let blocks: &mut [DataBlock] = - unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; - self.read_blocks(block_address, blocks).await?; - } - Ok(()) - } - - async fn write( - &mut self, - block_address: u32, - buf: &[aligned::Aligned], - ) -> Result<(), Self::Error> { - // TODO: I think block_address needs to be adjusted by the partition start offset - if buf.len() == 1 { - let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; - self.write_block(block_address, block).await?; - } else { - let blocks: &[DataBlock] = - unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; - self.write_blocks(block_address, blocks).await?; - } - Ok(()) - } - - async fn size(&mut self) -> Result { - Ok(self.info.size()) - } -} diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs new file mode 100644 index 000000000..6190226b8 --- /dev/null +++ b/embassy-stm32/src/sdmmc/sd.rs @@ -0,0 +1,693 @@ +use core::default::Default; +use core::ops::{Deref, DerefMut}; + +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::time::{Hertz, mhz}; + +/// Aligned data block for SDMMC transfers. +/// +/// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. +#[repr(align(4))] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DataBlock(pub [u32; 128]); + +impl DataBlock { + /// Create a new DataBlock + pub const fn new() -> Self { + DataBlock([0u32; 128]) + } +} + +impl Deref for DataBlock { + type Target = [u8; 512]; + + fn deref(&self) -> &Self::Target { + unwrap!(slice8_ref(&self.0[..]).try_into()) + } +} + +impl DerefMut for DataBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + unwrap!(slice8_mut(&mut self.0[..]).try_into()) + } +} + +/// Command Block buffer for SDMMC command transfers. +/// +/// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct CmdBlock(pub [u32; 16]); + +impl CmdBlock { + /// Creates a new instance of CmdBlock + pub const fn new() -> Self { + Self([0u32; 16]) + } +} + +impl Deref for CmdBlock { + type Target = [u32; 16]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CmdBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Represents either an SD or EMMC card +pub trait Addressable: Sized + Clone { + /// Associated type + type Ext; + + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16; + + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity; + + /// Size in bytes + fn size(&self) -> u64; +} + +/// Storage Device +pub struct StorageDevice<'a, 'b, T: Addressable> { + info: T, + /// Inner member + pub sdmmc: &'a mut Sdmmc<'b>, +} + +/// Card Storage Device +impl<'a, 'b> StorageDevice<'a, 'b, Card> { + /// Create a new SD card + pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { + let mut s = Self { + info: Card::default(), + sdmmc, + }; + + s.acquire(cmd_block, freq).await?; + + Ok(s) + } + + /// Initializes the card into a known state (or at least tries to). + async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let regs = self.sdmmc.info.regs; + + let _bus_width = match self.sdmmc.bus_width() { + BusWidth::Eight => return Err(Error::BusWidth), + bus_width => bus_width, + }; + + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + self.sdmmc.init_idle()?; + + // Check if cards supports CMD8 (with pattern) + self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; + let cic = CIC::from(regs.respr(0).read().cardstatus()); + + if cic.pattern() != 0xAA { + return Err(Error::UnsupportedCardVersion); + } + + if cic.voltage_accepted() & 1 == 0 { + return Err(Error::UnsupportedVoltage); + } + + let ocr = loop { + // Signal that next command is a app command + self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 + + // 3.2-3.3V + let voltage_window = 1 << 5; + // Initialize card + match self + .sdmmc + .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) + { + // ACMD41 + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; + + if ocr.high_capacity() { + // Card is SDHC or SDXC or SDUC + self.info.card_type = CardCapacity::HighCapacity; + } else { + self.info.card_type = CardCapacity::StandardCapacity; + } + self.info.ocr = ocr; + + self.info.cid = self.sdmmc.get_cid()?.into(); + + self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; + let rca = RCA::::from(regs.respr(0).read().cardstatus()); + self.info.rca = rca.address(); + + self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); + self.sdmmc.select_card(Some(self.info.get_address()))?; + + self.info.scr = self.get_scr(cmd_block).await?; + + let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { + (BusWidth::One, 0) + } else { + (BusWidth::Four, 2) + }; + + self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; + self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; + + self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; + + // Read status + self.info.status = self.read_sd_status(cmd_block).await?; + + if freq > mhz(25) { + // Switch to SDR25 + self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; + + if self.sdmmc.signalling == Signalling::SDR25 { + // Set final clock frequency + self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; + + if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { + return Err(Error::SignalingSwitchFailed); + } + } + + // Read status after signalling change + self.read_sd_status(cmd_block).await?; + } + + Ok(()) + } + + /// Switch mode using CMD6. + /// + /// Attempt to set a new signalling mode. The selected + /// signalling mode is returned. Expects the current clock + /// frequency to be > 12.5MHz. + /// + /// SD only. + async fn switch_signalling_mode( + &self, + cmd_block: &mut CmdBlock, + signalling: Signalling, + ) -> Result { + // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not + // necessary" + + let set_function = 0x8000_0000 + | match signalling { + // See PLSS v7_10 Table 4-11 + Signalling::DDR50 => 0xFF_FF04, + Signalling::SDR104 => 0xFF_1F03, + Signalling::SDR50 => 0xFF_1F02, + Signalling::SDR25 => 0xFF_FF01, + Signalling::SDR12 => 0xFF_FF00, + }; + + let buffer = &mut cmd_block.0[..64 / 4]; + + let transfer = self + .sdmmc + .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); + + self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + // Host is allowed to use the new functions at least 8 + // clocks after the end of the switch command + // transaction. We know the current clock period is < 80ns, + // so a total delay of 640ns is required here + for _ in 0..300 { + cortex_m::asm::nop(); + } + + // Function Selection of Function Group 1 + let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; + + match selection { + 0 => Ok(Signalling::SDR12), + 1 => Ok(Signalling::SDR25), + 2 => Ok(Signalling::SDR50), + 3 => Ok(Signalling::SDR104), + 4 => Ok(Signalling::DDR50), + _ => Err(Error::UnsupportedCardType), + } + } + + /// Reads the SCR register. + /// + /// SD only. + async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result { + // Read the 64-bit SCR register + self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 + self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; + + let scr = &mut cmd_block.0[..2]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + + let transfer = self.sdmmc.prepare_datapath_read(scr, BlockSize::Size8, false); + self.sdmmc.cmd(sd_cmd::send_scr(), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) + } + + /// Reads the SD Status (ACMD13) + /// + /// SD only. + async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result { + let rca = self.info.rca; + + self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 + self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP + + let buffer = &mut cmd_block.as_mut()[..64 / 4]; + + let transfer = self + .sdmmc + .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); + self.sdmmc.cmd(sd_cmd::sd_status(), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + for byte in cmd_block.iter_mut() { + *byte = u32::from_be(*byte); + } + + Ok(cmd_block.0.into()) + } +} + +/// Emmc storage device +impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { + /// Create a new EMMC card + pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result { + let mut s = Self { + info: Emmc::default(), + sdmmc, + }; + + s.acquire(cmd_block, freq).await?; + + Ok(s) + } + + async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let regs = self.sdmmc.info.regs; + + let bus_width = self.sdmmc.bus_width(); + + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + self.sdmmc.init_idle()?; + + let ocr = loop { + let high_voltage = 0b0 << 7; + let access_mode = 0b10 << 29; + let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; + // Initialize card + match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; + + self.info.capacity = if ocr.access_mode() == 0b10 { + // Card is SDHC or SDXC or SDUC + CardCapacity::HighCapacity + } else { + CardCapacity::StandardCapacity + }; + self.info.ocr = ocr; + + self.info.cid = self.sdmmc.get_cid()?.into(); + + self.info.rca = 1u16.into(); + self.sdmmc + .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; + + self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); + self.sdmmc.select_card(Some(self.info.get_address()))?; + + let (widbus, _) = bus_width_vals(bus_width); + + // Write bus width to ExtCSD byte 183 + self.sdmmc.cmd( + emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), + false, + )?; + + // Wait for ready after R1b response + loop { + let status = self.sdmmc.read_status(&self.info)?; + + if status.ready_for_data() { + break; + } + } + + self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; + self.info.ext_csd = self.read_ext_csd().await?; + + Ok(()) + } + + /// Gets the EXT_CSD register. + /// + /// eMMC only. + async fn read_ext_csd(&self) -> Result { + // Note: cmd_block can't be used because ExtCSD is too long to fit. + let mut data_block = DataBlock::new(); + + self.sdmmc + .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); + self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + Ok(data_block.0.into()) + } +} + +/// Card or Emmc storage device +impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { + /// Write a block + pub fn card(&self) -> A { + self.info.clone() + } + + /// Read a data block. + #[inline] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let card_capacity = self.info.get_capacity(); + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card_capacity { + CardCapacity::StandardCapacity => block_idx * size_of::() as u32, + _ => block_idx, + }; + 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); + self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + Ok(()) + } + + /// Read multiple data blocks. + #[inline] + pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + let card_capacity = self.info.get_capacity(); + + // NOTE(unsafe) reinterpret buffer as &mut [u32] + let buffer = unsafe { + core::slice::from_raw_parts_mut( + blocks.as_mut_ptr() as *mut u32, + blocks.len() * size_of::() / size_of::(), + ) + }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card_capacity { + CardCapacity::StandardCapacity => block_idx * size_of::() as u32, + _ => block_idx, + }; + 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); + self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + + self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 + self.sdmmc.clear_interrupt_flags(); + + Ok(()) + } + + /// Write a data block. + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> + where + CardStatus: From, + { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + // Always read 1 block of 512 bytes + // cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match self.info.get_capacity() { + CardCapacity::StandardCapacity => block_idx * size_of::() as u32, + _ => block_idx, + }; + self.sdmmc + .cmd(common_cmd::set_block_length(size_of::() as u32), false)?; // CMD16 + + // sdmmc_v1 uses different cmd/dma order than v2, but only for writes + #[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); + + #[cfg(sdmmc_v2)] + self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, true).await?; + + // TODO: Make this configurable + let mut timeout: u32 = 0x00FF_FFFF; + + while timeout > 0 { + let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); + if ready_for_data { + return Ok(()); + } + timeout -= 1; + } + + Err(Error::SoftwareTimeout) + } + + /// Write multiple data blocks. + pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> + where + CardStatus: From, + { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + // NOTE(unsafe) reinterpret buffer as &[u32] + let buffer = unsafe { + core::slice::from_raw_parts( + blocks.as_ptr() as *const u32, + blocks.len() * size_of::() / size_of::(), + ) + }; + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match self.info.get_capacity() { + CardCapacity::StandardCapacity => block_idx * size_of::() as u32, + _ => block_idx, + }; + + self.sdmmc + .cmd(common_cmd::set_block_length(size_of::() as u32), false)?; // CMD16 + + #[cfg(sdmmc_v1)] + 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); + + #[cfg(sdmmc_v2)] + self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + + self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 + self.sdmmc.clear_interrupt_flags(); + + // TODO: Make this configurable + let mut timeout: u32 = 0x00FF_FFFF; + + while timeout > 0 { + let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); + + if ready_for_data { + return Ok(()); + } + timeout -= 1; + } + Err(Error::SoftwareTimeout) + } +} + +#[derive(Clone, Copy, Debug, Default)] +/// SD Card +pub struct Card { + /// The type of this card + pub card_type: CardCapacity, + /// Operation Conditions Register + pub ocr: OCR, + /// Relative Card Address + pub rca: u16, + /// Card ID + pub cid: CID, + /// Card Specific Data + pub csd: CSD, + /// SD CARD Configuration Register + pub scr: SCR, + /// SD Status + pub status: SDStatus, +} + +impl Addressable for Card { + type Ext = SD; + + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16 { + self.rca + } + + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity { + self.card_type + } + + /// Size in bytes + fn size(&self) -> u64 { + u64::from(self.csd.block_count()) * 512 + } +} + +#[derive(Clone, Copy, Debug, Default)] +/// eMMC storage +pub struct Emmc { + /// The capacity of this card + pub capacity: CardCapacity, + /// Operation Conditions Register + pub ocr: OCR, + /// Relative Card Address + pub rca: u16, + /// Card ID + pub cid: CID, + /// Card Specific Data + pub csd: CSD, + /// Extended Card Specific Data + pub ext_csd: ExtCSD, +} + +impl Addressable for Emmc { + type Ext = EMMC; + + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16 { + self.rca + } + + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity { + self.capacity + } + + /// Size in bytes + fn size(&self) -> u64 { + u64::from(self.ext_csd.sector_count()) * 512 + } +} + +impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { + type Error = Error; + type Align = aligned::A4; + + async fn read( + &mut self, + block_address: u32, + buf: &mut [aligned::Aligned], + ) -> Result<(), Self::Error> { + // TODO: I think block_address needs to be adjusted by the partition start offset + if buf.len() == 1 { + let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) }; + self.read_block(block_address, block).await?; + } else { + let blocks: &mut [DataBlock] = + unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; + self.read_blocks(block_address, blocks).await?; + } + Ok(()) + } + + async fn write( + &mut self, + block_address: u32, + buf: &[aligned::Aligned], + ) -> Result<(), Self::Error> { + // TODO: I think block_address needs to be adjusted by the partition start offset + if buf.len() == 1 { + let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) }; + self.write_block(block_address, block).await?; + } else { + let blocks: &[DataBlock] = + unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; + self.write_blocks(block_address, blocks).await?; + } + Ok(()) + } + + async fn size(&mut self) -> Result { + Ok(self.info.size()) + } +} diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs new file mode 100644 index 000000000..1412b21fc --- /dev/null +++ b/embassy-stm32/src/sdmmc/sdio.rs @@ -0,0 +1,177 @@ +use core::ops::{Deref, DerefMut}; + +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::time::Hertz; + +/// Aligned data block for SDMMC transfers. +/// +/// This is a 64-byte array, aligned to 4 bytes to satisfy DMA requirements. +#[repr(align(4))] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DataBlock(pub [u32; 16]); + +impl DataBlock { + /// Create a new DataBlock + pub const fn new() -> Self { + DataBlock([0u32; 16]) + } +} + +impl Deref for DataBlock { + type Target = [u8; 64]; + + fn deref(&self) -> &Self::Target { + unwrap!(slice8_ref(&self.0[..]).try_into()) + } +} + +impl DerefMut for DataBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + unwrap!(slice8_mut(&mut self.0[..]).try_into()) + } +} + +/// Storage Device +pub struct SerialDataInterface<'a, 'b> { + /// Inner member + pub sdmmc: &'a mut Sdmmc<'b>, +} + +/// Card Storage Device +impl<'a, 'b> SerialDataInterface<'a, 'b> { + /// Create a new SD card + pub async fn new(sdmmc: &'a mut Sdmmc<'b>, freq: Hertz) -> Result { + let mut s = Self { sdmmc }; + + s.acquire(freq).await?; + + Ok(s) + } + + /// Initializes the card into a known state (or at least tries to). + async fn acquire(&mut self, _freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + let _bus_width = match self.sdmmc.bus_width() { + BusWidth::Eight => return Err(Error::BusWidth), + bus_width => bus_width, + }; + + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + self.sdmmc.init_idle()?; + + self.sdmmc.cmd(cmd::(5, 0), false)?; + + // Get RCA + let rca = self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; + + // Select the card with RCA + self.sdmmc.select_card(Some(rca.try_into().unwrap()))?; + + Ok(()) + } + + /// Set the bus to the 4-bit high-speed frequency + pub fn set_bus_to_high_speed(&mut self, frequency: Hertz) -> Result<(), Error> { + self.sdmmc.clkcr_set_clkdiv(frequency, BusWidth::Four)?; + + Ok(()) + } + + /// Run cmd52 + pub async fn cmd52(&mut self, arg: u32) -> Result { + self.sdmmc.cmd(cmd::(52, arg), false) + } + + /// Read in block mode using cmd53 + pub async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + // NOTE(unsafe) reinterpret buffer as &mut [u32] + let buffer = unsafe { + core::slice::from_raw_parts_mut( + blocks.as_mut_ptr() as *mut u32, + blocks.len() * size_of::() / size_of::(), + ) + }; + + let transfer = self + .sdmmc + .prepare_datapath_read(buffer, block_size(size_of::()), false); + self.sdmmc.cmd(cmd::(53, arg), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + self.sdmmc.clear_interrupt_flags(); + + Ok(()) + } + + /// Read in multibyte mode using cmd53 + pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut [u32]) -> 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); + self.sdmmc.cmd(cmd::(53, arg), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + self.sdmmc.clear_interrupt_flags(); + + Ok(()) + } + + /// Write in block mode using cmd53 + pub async fn cmd53_block_write(&mut self, arg: u32, blocks: &[DataBlock]) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + // NOTE(unsafe) reinterpret buffer as &mut [u32] + let buffer = unsafe { + core::slice::from_raw_parts_mut( + blocks.as_ptr() as *mut u32, + blocks.len() * size_of::() / size_of::(), + ) + }; + + #[cfg(sdmmc_v1)] + self.sdmmc.cmd(cmd::(53, arg), true)?; + + let transfer = self + .sdmmc + .prepare_datapath_read(buffer, block_size(size_of::()), false); + + #[cfg(sdmmc_v2)] + self.sdmmc.cmd(cmd::(53, arg), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + self.sdmmc.clear_interrupt_flags(); + + Ok(()) + } + + /// Write in multibyte mode using cmd53 + pub async fn cmd53_byte_write(&mut self, arg: u32, buffer: &[u32]) -> Result<(), Error> { + let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); + + #[cfg(sdmmc_v1)] + self.sdmmc.cmd(cmd::(53, arg), true)?; + + let transfer = self + .sdmmc + .prepare_datapath_write(buffer, block_size(size_of::()), true); + + #[cfg(sdmmc_v2)] + self.sdmmc.cmd(cmd::(53, arg), true)?; + + self.sdmmc.complete_datapath_transfer(transfer, false).await?; + self.sdmmc.clear_interrupt_flags(); + + Ok(()) + } +} diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 897b0e05e..098fd6986 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -3,7 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::{CmdBlock, DataBlock, Sdmmc, StorageDevice}; +use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; use embassy_stm32::time::{Hertz, mhz}; use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; @@ -72,7 +73,7 @@ async fn main(_spawner: Spawner) { let block_idx = 16; // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. - let mut block = DataBlock([0u8; 512]); + let mut block = DataBlock::new(); storage.read_block(block_idx, &mut block).await.unwrap(); info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 0dd4dd6f3..e5d261d89 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -3,7 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::{CmdBlock, Sdmmc, StorageDevice}; +use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; use embassy_stm32::time::{Hertz, mhz}; use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 548d0bd09..f2e5bedeb 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -3,7 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::{CmdBlock, Sdmmc, StorageDevice}; +use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; use embassy_stm32::time::mhz; use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index d98b93a15..07422c42e 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -7,7 +7,8 @@ mod common; use common::*; use defmt::assert_eq; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::{CmdBlock, DataBlock, Sdmmc, StorageDevice}; +use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; @@ -28,16 +29,16 @@ async fn main(_spawner: Spawner) { // Arbitrary block index let block_idx = 16; - let mut pattern1 = DataBlock([0u8; 512]); - let mut pattern2 = DataBlock([0u8; 512]); + let mut pattern1 = DataBlock::new(); + let mut pattern2 = DataBlock::new(); for i in 0..512 { pattern1[i] = i as u8; pattern2[i] = !i as u8; } let patterns = [pattern1.clone(), pattern2.clone()]; - let mut block = DataBlock([0u8; 512]); - let mut blocks = [DataBlock([0u8; 512]), DataBlock([0u8; 512])]; + let mut block = DataBlock::new(); + let mut blocks = [DataBlock::new(), DataBlock::new()]; // ======== Try 4bit. ============== info!("initializing in 4-bit mode..."); -- cgit From efdfb3c0892f45223e8d8527e5f032fef475944e Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 15:34:38 +0100 Subject: Refactor the API --- embassy-mcxa/src/adc.rs | 642 +++++++++++++++++++-------------- examples/mcxa/src/bin/adc_interrupt.rs | 20 +- examples/mcxa/src/bin/adc_polling.rs | 26 +- 3 files changed, 387 insertions(+), 301 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index d7d17cf5f..e4b24cffa 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -1,4 +1,5 @@ //! ADC driver +use core::future::Future; use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; @@ -12,12 +13,10 @@ use crate::interrupt::typelevel::{Handler, Interrupt}; use crate::pac; use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; -use crate::pac::adc1::cmdl1::{Adch, Mode}; +use crate::pac::adc1::cmdl1::Mode; use crate::pac::adc1::ctrl::CalAvgs; use crate::pac::adc1::tctrl::{Tcmd, Tpri}; -const G_LPADC_RESULT_SHIFT: u32 = 0; - /// Trigger priority policy for ADC conversions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -59,10 +58,6 @@ pub struct LpadcConfig { /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. pub conv_pause_delay: u16, - /// FIFO watermark level for interrupt generation. - /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field, - /// the ready flag would be asserted to indicate stored data has reached the programmable threshold. - pub fifo_watermark: u8, /// Power configuration (normal/deep sleep behavior) pub power: PoweredClock, /// ADC clock source selection @@ -83,7 +78,6 @@ impl Default for LpadcConfig { trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, enable_conv_pause: false, conv_pause_delay: 0, - fifo_watermark: 0, power: PoweredClock::NormalEnabledDeepSleepDisabled, source: AdcClockSel::FroLfDiv, div: Div4::no_div(), @@ -96,7 +90,6 @@ impl Default for LpadcConfig { /// Defines the parameters for a single ADC conversion operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvCommandConfig { - pub channel_number: Adch, pub chained_next_command_number: Next, pub enable_auto_channel_increment: bool, pub loop_count: u8, @@ -109,6 +102,23 @@ pub struct ConvCommandConfig { pub enable_wait_trigger: bool, } +impl Default for ConvCommandConfig { + fn default() -> Self { + ConvCommandConfig { + chained_next_command_number: Next::NoNextCmdTerminateOnFinish, + enable_auto_channel_increment: false, + loop_count: 0, + hardware_average_mode: Avgs::NoAverage, + sample_time_mode: Sts::Sample3p5, + hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, + hardware_compare_value_high: 0, + hardware_compare_value_low: 0, + conversion_resolution_mode: Mode::Data12Bits, + enable_wait_trigger: false, + } + } +} + /// Configuration for a conversion trigger. /// /// Defines how a trigger initiates ADC conversions. @@ -120,6 +130,17 @@ pub struct ConvTriggerConfig { pub enable_hardware_trigger: bool, } +impl Default for ConvTriggerConfig { + fn default() -> Self { + ConvTriggerConfig { + target_command_id: Tcmd::NotValid, + delay_power: 0, + priority: Tpri::HighestPriority, + enable_hardware_trigger: false, + } + } +} + /// Shorthand for `Result`. pub type Result = core::result::Result; @@ -155,6 +176,7 @@ pub struct InterruptHandler { pub struct Adc<'a, I: Instance, M: ModeAdc> { _inst: PhantomData<&'a mut I>, _phantom: PhantomData, + index: u8, } impl<'a, I: Instance> Adc<'a, I, Blocking> { @@ -166,6 +188,104 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { pub fn new_blocking(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Result { Self::new_inner(_inst, pin, config) } + + /// Enable ADC interrupts. + /// + /// Enables the interrupt sources specified in the bitmask. + /// + /// # Arguments + /// * `mask` - Bitmask of interrupt sources to enable + pub fn enable_interrupt(&mut self, mask: u32) { + let adc = I::ptr(); + adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); + } + + /// Disable ADC interrupts. + /// + /// Disables the interrupt sources specified in the bitmask. + /// + /// # Arguments + /// * `mask` - Bitmask of interrupt sources to disable + pub fn disable_interrupt(&mut self, mask: u32) { + let adc = I::ptr(); + adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); + } + + pub fn set_fifo_watermark(&mut self, watermark: u8) -> Result<()> { + if watermark > 0b111 { + return Err(Error::InvalidConfig); + } + I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) }); + Ok(()) + } + + /// Trigger ADC conversion(s) via software. + /// + /// Initiates conversion(s) for the trigger(s) specified in the bitmask. + /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). + /// + /// # Arguments + /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) + /// + /// # Returns + /// * `Ok(())` if the triger mask was valid + /// * `Err(Error::InvalidConfig)` if the mask was greater than `0b1111` + pub fn do_software_trigger(&self, trigger_id_mask: u8) -> Result<()> { + if trigger_id_mask > 0b1111 { + return Err(Error::InvalidConfig); + } + let adc = I::ptr(); + adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) }); + Ok(()) + } + + /// Set conversion command configuration. + /// + /// Configures a conversion command slot with the specified parameters. + /// Commands define how conversions are performed (channel, resolution, etc.). + /// + /// # Arguments + /// * `index` - Command index (Must be in range 1..=7) + /// * `config` - Command configuration + /// + /// # Returns + /// * `Ok(())` if the command was configured successfully + /// * `Err(Error::InvalidConfig)` if the index is out of range + pub fn set_conv_command_config(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { + self.set_conv_command_config_inner(index, config) + } + + /// Set conversion trigger configuration. + /// + /// Configures a trigger to initiate conversions. Triggers can be + /// activated by software or hardware signals. + /// + /// # Arguments + /// * `trigger_id` - Trigger index (0..=3) + /// * `config` - Trigger configuration + pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { + self.set_conv_trigger_config_inner(trigger_id, config) + } + + /// Reset the FIFO buffer. + /// + /// Clears all pending conversion results from the FIFO. + pub fn do_reset_fifo(&self) { + let adc = I::ptr(); + adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); + } + + /// Get conversion result from FIFO. + /// + /// Reads and returns the next conversion result from the FIFO. + /// Returns `None` if the FIFO is empty. + /// + /// # Returns + /// - `Some(ConvResult)` if a result is available + /// - `Err(Error::FifoEmpty)` if the FIFO is empty + pub fn get_conv_result(&self) -> Result { + self.get_conv_result_inner() + } } impl<'a, I: Instance> Adc<'a, I, Async> { @@ -182,12 +302,65 @@ impl<'a, I: Instance> Adc<'a, I, Async> { _irq: impl crate::interrupt::typelevel::Binding> + 'a, config: LpadcConfig, ) -> Result { - let adc = Self::new_inner(_inst, pin, config); + let adc = Self::new_inner(_inst, pin, config)?; I::Interrupt::unpend(); unsafe { I::Interrupt::enable() }; - adc + let cfg = ConvCommandConfig { + chained_next_command_number: Next::NoNextCmdTerminateOnFinish, + enable_auto_channel_increment: false, + loop_count: 0, + hardware_average_mode: Avgs::NoAverage, // todo: good config? + sample_time_mode: Sts::Sample3p5, // todo: good config? + hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, + hardware_compare_value_high: 0, + hardware_compare_value_low: 0, + conversion_resolution_mode: Mode::Data16Bits, // todo: good config? + enable_wait_trigger: false, + }; + + // We always use command 1, so this cannot fail + _ = adc.set_conv_command_config_inner(1, &cfg); + + let cfg = ConvTriggerConfig { + target_command_id: Tcmd::ExecuteCmd1, + delay_power: 0, + priority: Tpri::HighestPriority, + enable_hardware_trigger: false, + }; + + // We always use trigger 0, so this cannot fail + _ = adc.set_conv_trigger_config_inner(0, &cfg); + + // We always set the watermark to 0 (trigger when 1 is available) + I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(0) }); + + Ok(adc) + } + + /// Set the number of averages + pub fn set_averages(&mut self, avgs: Avgs) { + // TODO: we should probably return a result or wait for idle? + // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." + I::ptr().cmdh1().modify(|_r, w| w.avgs().variant(avgs)); + } + + /// Set the sample time + pub fn set_sample_time(&mut self, st: Sts) { + // TODO: we should probably return a result or wait for idle? + // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." + I::ptr().cmdh1().modify(|_r, w| w.sts().variant(st)); + } + + pub fn set_resolution(&mut self, mode: Mode) { + // TODO: we should probably return a result or wait for idle? + // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." + I::ptr().cmdl1().modify(|_r, w| w.mode().variant(mode)); + } + + fn wait_idle(&mut self) -> impl Future> + use<'_, I> { + I::wait_cell().wait_for(|| I::ptr().ie().read().fwmie0().bit_is_clear()) } /// Read ADC value asynchronously. @@ -203,21 +376,29 @@ impl<'a, I: Instance> Adc<'a, I, Async> { /// # Returns /// 16-bit ADC conversion value pub async fn read(&mut self) -> Result { - let wait = I::wait_cell().subscribe().await; + let adc = I::ptr(); - Adc::<'a, I, Async>::enable_interrupt(self, 0x1); - Adc::<'a, I, Async>::do_software_trigger(self, 1); + // If we cancelled a previous read, we might still be busy, wait + // until the interrupt is cleared (done by the interrupt) + _ = self.wait_idle().await; - let _ = wait.await; + // Clear the fifo + adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); + + // Trigger a new conversion + adc.ie().modify(|_r, w| w.fwmie0().set_bit()); + adc.swtrig().write(|w| w.swt0().set_bit()); - let result = Adc::<'a, I, Async>::get_conv_result(self).unwrap().conv_value >> G_LPADC_RESULT_SHIFT; - Ok(result) + // Wait for completion + _ = self.wait_idle().await; + + self.get_conv_result_inner().map(|r| r.conv_value) } } impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// Internal initialization function shared by `new_async` and `new_blocking`. - fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Result { + fn new_inner>(_inst: Peri<'a, I>, pin: Peri<'a, P>, config: LpadcConfig) -> Result { let adc = I::ptr(); _ = unsafe { @@ -241,22 +422,16 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { adc.ctrl().modify(|_, w| w.adcen().disabled()); /* Configure the module generally. */ - if config.enable_in_doze_mode { - adc.ctrl().modify(|_, w| w.dozen().enabled()); - } else { - adc.ctrl().modify(|_, w| w.dozen().disabled()); - } + adc.ctrl().modify(|_, w| w.dozen().bit(config.enable_in_doze_mode)); /* Set calibration average mode. */ adc.ctrl() .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); adc.cfg().write(|w| unsafe { - let w = if config.enable_analog_preliminary { - w.pwren().pre_enabled() - } else { - w - }; + if config.enable_analog_preliminary { + w.pwren().pre_enabled(); + } w.pudly() .bits(config.power_up_delay) @@ -306,8 +481,7 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { adc.pause().write(|w| unsafe { w.bits(0) }); } - adc.fctrl0() - .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) }); + adc.fctrl0().write(|w| unsafe { w.fwmark().bits(0) }); // Enable ADC adc.ctrl().modify(|_, w| w.adcen().enabled()); @@ -315,15 +489,10 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { Ok(Self { _inst: PhantomData, _phantom: PhantomData, + index: P::CHANNEL, }) } - /// Deinitialize the ADC peripheral. - pub fn deinit(&self) { - let adc = I::ptr(); - adc.ctrl().modify(|_, w| w.adcen().disabled()); - } - /// Perform offset calibration. /// Waits for calibration to complete before returning. pub fn do_offset_calibration(&self) { @@ -369,7 +538,7 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} let mut gcca = adc.gcc0().read().gain_cal().bits() as u32; - if gcca & ((0xFFFF + 1) >> 1) != 0 { + if gcca & 0x8000 != 0 { gcca |= !0xFFFF; } @@ -384,127 +553,58 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { while adc.stat().read().cal_rdy().is_not_set() {} } - /// Trigger ADC conversion(s) via software. - /// - /// Initiates conversion(s) for the trigger(s) specified in the bitmask. - /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). - /// - /// # Arguments - /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) - pub fn do_software_trigger(&self, trigger_id_mask: u32) { + fn set_conv_command_config_inner(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { let adc = I::ptr(); - adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); - } - - /// Get default conversion command configuration. - /// # Returns - /// Default conversion command configuration - pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { - ConvCommandConfig { - channel_number: Adch::SelectCh0, - chained_next_command_number: Next::NoNextCmdTerminateOnFinish, - enable_auto_channel_increment: false, - loop_count: 0, - hardware_average_mode: Avgs::NoAverage, - sample_time_mode: Sts::Sample3p5, - hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, - hardware_compare_value_high: 0, - hardware_compare_value_low: 0, - conversion_resolution_mode: Mode::Data12Bits, - enable_wait_trigger: false, - } - } - /// Set conversion command configuration. - /// - /// Configures a conversion command slot with the specified parameters. - /// Commands define how conversions are performed (channel, resolution, etc.). - /// - /// # Arguments - /// * `index` - Command index - /// * `config` - Command configuration - /// - /// # Returns - /// * `Ok(())` if the command was configured successfully - /// * `Err(Error::InvalidConfig)` if the index is out of range - pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) -> Result<()> { - let adc = I::ptr(); - - if index < 1 || index > 7 { - return Err(Error::InvalidConfig); - } + let (cmdl, cmdh) = match index { + 1 => (adc.cmdl1(), adc.cmdh1()), + 2 => (adc.cmdl2(), adc.cmdh2()), + 3 => (adc.cmdl3(), adc.cmdh3()), + 4 => (adc.cmdl4(), adc.cmdh4()), + 5 => (adc.cmdl5(), adc.cmdh5()), + 6 => (adc.cmdl6(), adc.cmdh6()), + 7 => (adc.cmdl7(), adc.cmdh7()), + _ => return Err(Error::InvalidConfig), + }; - macro_rules! write_cmd { - ($idx:expr) => {{ - paste! { - adc.[]().write(|w| { - w.adch() - .variant(config.channel_number) - .mode() - .variant(config.conversion_resolution_mode) - }); - adc.[]().write(|w| unsafe { - w.next() - .variant(config.chained_next_command_number) - .loop_() - .bits(config.loop_count) - .avgs() - .variant(config.hardware_average_mode) - .sts() - .variant(config.sample_time_mode) - .cmpen() - .variant(config.hardware_compare_mode) - .wait_trig() - .bit(config.enable_wait_trigger) - .lwi() - .bit(config.enable_auto_channel_increment) - }); - } - }}; - } + cmdl.write(|w| { + unsafe { + w.adch().bits(self.index); + } + w.mode().variant(config.conversion_resolution_mode) + }); - match index { - 1 => write_cmd!(1), - 2 => write_cmd!(2), - 3 => write_cmd!(3), - 4 => write_cmd!(4), - 5 => write_cmd!(5), - 6 => write_cmd!(6), - 7 => write_cmd!(7), - _ => unreachable!(), - } + cmdh.write(|w| { + w.next().variant(config.chained_next_command_number); + unsafe { + w.loop_().bits(config.loop_count); + } + w.avgs().variant(config.hardware_average_mode); + w.sts().variant(config.sample_time_mode); + w.cmpen().variant(config.hardware_compare_mode); + w.wait_trig().bit(config.enable_wait_trigger); + w.lwi().bit(config.enable_auto_channel_increment); + w + }); Ok(()) } - /// Get default conversion trigger configuration. - /// - /// # Returns - /// Default conversion trigger configuration - pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { - ConvTriggerConfig { - target_command_id: Tcmd::NotValid, - delay_power: 0, - priority: Tpri::HighestPriority, - enable_hardware_trigger: false, + fn set_conv_trigger_config_inner(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { + let adc = I::ptr(); + + // 0..4 are valid + if trigger_id >= 4 { + return Err(Error::InvalidConfig); } - } - /// Set conversion trigger configuration. - /// - /// Configures a trigger to initiate conversions. Triggers can be - /// activated by software or hardware signals. - /// - /// # Arguments - /// * `trigger_id` - Trigger index (0-15) - /// * `config` - Trigger configuration - pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { - let adc = I::ptr(); let tctrl = &adc.tctrl(trigger_id); - tctrl.write(|w| unsafe { - let w = w.tcmd().variant(config.target_command_id); - let w = w.tdly().bits(config.delay_power); + tctrl.write(|w| { + w.tcmd().variant(config.target_command_id); + unsafe { + w.tdly().bits(config.delay_power); + } w.tpri().variant(config.priority); if config.enable_hardware_trigger { w.hten().enabled() @@ -512,36 +612,8 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { w } }); - } - /// Reset the FIFO buffer. - /// - /// Clears all pending conversion results from the FIFO. - pub fn do_reset_fifo(&self) { - let adc = I::ptr(); - adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); - } - - /// Enable ADC interrupts. - /// - /// Enables the interrupt sources specified in the bitmask. - /// - /// # Arguments - /// * `mask` - Bitmask of interrupt sources to enable - pub fn enable_interrupt(&self, mask: u32) { - let adc = I::ptr(); - adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); - } - - /// Disable ADC interrupts. - /// - /// Disables the interrupt sources specified in the bitmask. - /// - /// # Arguments - /// * `mask` - Bitmask of interrupt sources to disable - pub fn disable_interrupt(&self, mask: u32) { - let adc = I::ptr(); - adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); + Ok(()) } /// Get conversion result from FIFO. @@ -552,7 +624,7 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// # Returns /// - `Some(ConvResult)` if a result is available /// - `Err(Error::FifoEmpty)` if the FIFO is empty - pub fn get_conv_result(&self) -> Result { + fn get_conv_result_inner(&self) -> Result { let adc = I::ptr(); let fifo = adc.resfifo0().read(); if !fifo.valid().is_valid() { @@ -570,7 +642,7 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { impl Handler for InterruptHandler { unsafe fn on_interrupt() { - T::ptr().ie().modify(|r, w| w.bits(r.bits() & !0x1)); + T::ptr().ie().modify(|_r, w| w.fwmie0().clear_bit()); T::wait_cell().wake(); } } @@ -621,6 +693,8 @@ macro_rules! impl_instance { impl_instance!(0, 1, 2, 3); pub trait AdcPin: GpioPin + sealed::Sealed + PeripheralType { + const CHANNEL: u8; + /// Set the given pin to the correct muxing state fn mux(&self); } @@ -640,8 +714,10 @@ impl sealed::Sealed for Async {} impl ModeAdc for Async {} macro_rules! impl_pin { - ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { - impl $trait for crate::peripherals::$pin { + ($pin:ident, $peri:ident, $func:ident, $channel:literal) => { + impl AdcPin for crate::peripherals::$pin { + const CHANNEL: u8 = $channel; + fn mux(&self) { self.set_pull(crate::gpio::Pull::Disabled); self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); @@ -652,101 +728,113 @@ macro_rules! impl_pin { }; } -impl_pin!(P2_0, ADC0, Mux0, AdcPin); -impl_pin!(P2_4, ADC0, Mux0, AdcPin); -impl_pin!(P2_15, ADC0, Mux0, AdcPin); -impl_pin!(P2_3, ADC0, Mux0, AdcPin); -impl_pin!(P2_2, ADC0, Mux0, AdcPin); -impl_pin!(P2_12, ADC0, Mux0, AdcPin); -impl_pin!(P2_16, ADC0, Mux0, AdcPin); -impl_pin!(P2_7, ADC0, Mux0, AdcPin); -impl_pin!(P0_18, ADC0, Mux0, AdcPin); -impl_pin!(P0_19, ADC0, Mux0, AdcPin); -impl_pin!(P0_20, ADC0, Mux0, AdcPin); -impl_pin!(P0_21, ADC0, Mux0, AdcPin); -impl_pin!(P0_22, ADC0, Mux0, AdcPin); -impl_pin!(P0_23, ADC0, Mux0, AdcPin); -impl_pin!(P0_3, ADC0, Mux0, AdcPin); -impl_pin!(P0_6, ADC0, Mux0, AdcPin); -impl_pin!(P1_0, ADC0, Mux0, AdcPin); -impl_pin!(P1_1, ADC0, Mux0, AdcPin); -impl_pin!(P1_2, ADC0, Mux0, AdcPin); -impl_pin!(P1_3, ADC0, Mux0, AdcPin); -impl_pin!(P1_4, ADC0, Mux0, AdcPin); -impl_pin!(P1_5, ADC0, Mux0, AdcPin); -impl_pin!(P1_6, ADC0, Mux0, AdcPin); -impl_pin!(P1_7, ADC0, Mux0, AdcPin); -impl_pin!(P1_10, ADC0, Mux0, AdcPin); - -impl_pin!(P2_1, ADC1, Mux0, AdcPin); -impl_pin!(P2_5, ADC1, Mux0, AdcPin); -impl_pin!(P2_19, ADC1, Mux0, AdcPin); -impl_pin!(P2_6, ADC1, Mux0, AdcPin); -impl_pin!(P2_3, ADC1, Mux0, AdcPin); -impl_pin!(P2_13, ADC1, Mux0, AdcPin); -impl_pin!(P2_17, ADC1, Mux0, AdcPin); -impl_pin!(P2_7, ADC1, Mux0, AdcPin); -impl_pin!(P1_10, ADC1, Mux0, AdcPin); -impl_pin!(P1_11, ADC1, Mux0, AdcPin); -impl_pin!(P1_12, ADC1, Mux0, AdcPin); -impl_pin!(P1_13, ADC1, Mux0, AdcPin); -impl_pin!(P1_14, ADC1, Mux0, AdcPin); -impl_pin!(P1_15, ADC1, Mux0, AdcPin); -impl_pin!(P1_16, ADC1, Mux0, AdcPin); -impl_pin!(P1_17, ADC1, Mux0, AdcPin); -impl_pin!(P1_18, ADC1, Mux0, AdcPin); -impl_pin!(P1_19, ADC1, Mux0, AdcPin); -impl_pin!(P3_31, ADC1, Mux0, AdcPin); -impl_pin!(P3_30, ADC1, Mux0, AdcPin); -impl_pin!(P3_29, ADC1, Mux0, AdcPin); - -impl_pin!(P2_4, ADC2, Mux0, AdcPin); -impl_pin!(P2_10, ADC2, Mux0, AdcPin); -impl_pin!(P4_4, ADC2, Mux0, AdcPin); -impl_pin!(P2_24, ADC2, Mux0, AdcPin); -impl_pin!(P2_16, ADC2, Mux0, AdcPin); -impl_pin!(P2_12, ADC2, Mux0, AdcPin); -impl_pin!(P2_20, ADC2, Mux0, AdcPin); -impl_pin!(P2_7, ADC2, Mux0, AdcPin); -impl_pin!(P0_2, ADC2, Mux0, AdcPin); -impl_pin!(P0_4, ADC2, Mux0, AdcPin); -impl_pin!(P0_5, ADC2, Mux0, AdcPin); -impl_pin!(P0_6, ADC2, Mux0, AdcPin); -impl_pin!(P0_7, ADC2, Mux0, AdcPin); -impl_pin!(P0_12, ADC2, Mux0, AdcPin); -impl_pin!(P0_13, ADC2, Mux0, AdcPin); -impl_pin!(P0_14, ADC2, Mux0, AdcPin); -impl_pin!(P0_15, ADC2, Mux0, AdcPin); -impl_pin!(P4_0, ADC2, Mux0, AdcPin); -impl_pin!(P4_1, ADC2, Mux0, AdcPin); -impl_pin!(P4_2, ADC2, Mux0, AdcPin); -impl_pin!(P4_3, ADC2, Mux0, AdcPin); -//impl_pin!(P4_4, ADC2, Mux0, AdcPin); // Conflit with ADC2_A3 and ADC2_A20 using the same pin -impl_pin!(P4_5, ADC2, Mux0, AdcPin); -impl_pin!(P4_6, ADC2, Mux0, AdcPin); -impl_pin!(P4_7, ADC2, Mux0, AdcPin); - -impl_pin!(P2_5, ADC3, Mux0, AdcPin); -impl_pin!(P2_11, ADC3, Mux0, AdcPin); -impl_pin!(P2_23, ADC3, Mux0, AdcPin); -impl_pin!(P2_25, ADC3, Mux0, AdcPin); -impl_pin!(P2_17, ADC3, Mux0, AdcPin); -impl_pin!(P2_13, ADC3, Mux0, AdcPin); -impl_pin!(P2_21, ADC3, Mux0, AdcPin); -impl_pin!(P2_7, ADC3, Mux0, AdcPin); -impl_pin!(P3_2, ADC3, Mux0, AdcPin); -impl_pin!(P3_3, ADC3, Mux0, AdcPin); -impl_pin!(P3_4, ADC3, Mux0, AdcPin); -impl_pin!(P3_5, ADC3, Mux0, AdcPin); -impl_pin!(P3_6, ADC3, Mux0, AdcPin); -impl_pin!(P3_7, ADC3, Mux0, AdcPin); -impl_pin!(P3_12, ADC3, Mux0, AdcPin); -impl_pin!(P3_13, ADC3, Mux0, AdcPin); -impl_pin!(P3_14, ADC3, Mux0, AdcPin); -impl_pin!(P3_15, ADC3, Mux0, AdcPin); -impl_pin!(P3_20, ADC3, Mux0, AdcPin); -impl_pin!(P3_21, ADC3, Mux0, AdcPin); -impl_pin!(P3_22, ADC3, Mux0, AdcPin); -impl_pin!(P3_23, ADC3, Mux0, AdcPin); -impl_pin!(P3_24, ADC3, Mux0, AdcPin); -impl_pin!(P3_25, ADC3, Mux0, AdcPin); +impl_pin!(P2_0, ADC0, Mux0, 0); +impl_pin!(P2_4, ADC0, Mux0, 1); +impl_pin!(P2_15, ADC0, Mux0, 2); +impl_pin!(P2_3, ADC0, Mux0, 3); +impl_pin!(P2_2, ADC0, Mux0, 4); +impl_pin!(P2_12, ADC0, Mux0, 5); +impl_pin!(P2_16, ADC0, Mux0, 6); +impl_pin!(P2_7, ADC0, Mux0, 7); +impl_pin!(P0_18, ADC0, Mux0, 8); +impl_pin!(P0_19, ADC0, Mux0, 9); +impl_pin!(P0_20, ADC0, Mux0, 10); +impl_pin!(P0_21, ADC0, Mux0, 11); +impl_pin!(P0_22, ADC0, Mux0, 12); +impl_pin!(P0_23, ADC0, Mux0, 13); +impl_pin!(P0_3, ADC0, Mux0, 14); +impl_pin!(P0_6, ADC0, Mux0, 15); +impl_pin!(P1_0, ADC0, Mux0, 16); +impl_pin!(P1_1, ADC0, Mux0, 17); +impl_pin!(P1_2, ADC0, Mux0, 18); +impl_pin!(P1_3, ADC0, Mux0, 19); +impl_pin!(P1_4, ADC0, Mux0, 20); +impl_pin!(P1_5, ADC0, Mux0, 21); +impl_pin!(P1_6, ADC0, Mux0, 22); +impl_pin!(P1_7, ADC0, Mux0, 23); + +// ??? +// impl_pin!(P1_10, ADC0, Mux0, 255); + +impl_pin!(P2_1, ADC1, Mux0, 0); +impl_pin!(P2_5, ADC1, Mux0, 1); +impl_pin!(P2_19, ADC1, Mux0, 2); +impl_pin!(P2_6, ADC1, Mux0, 3); +impl_pin!(P2_3, ADC1, Mux0, 4); +impl_pin!(P2_13, ADC1, Mux0, 5); +impl_pin!(P2_17, ADC1, Mux0, 6); +impl_pin!(P2_7, ADC1, Mux0, 7); +impl_pin!(P1_10, ADC1, Mux0, 8); +impl_pin!(P1_11, ADC1, Mux0, 9); +impl_pin!(P1_12, ADC1, Mux0, 10); +impl_pin!(P1_13, ADC1, Mux0, 11); +impl_pin!(P1_14, ADC1, Mux0, 12); +impl_pin!(P1_15, ADC1, Mux0, 13); +// ??? +// impl_pin!(P1_16, ADC1, Mux0, 255); +// impl_pin!(P1_17, ADC1, Mux0, 255); +// impl_pin!(P1_18, ADC1, Mux0, 255); +// impl_pin!(P1_19, ADC1, Mux0, 255); +// ??? +impl_pin!(P3_31, ADC1, Mux0, 20); +impl_pin!(P3_30, ADC1, Mux0, 21); +impl_pin!(P3_29, ADC1, Mux0, 22); + +impl_pin!(P2_4, ADC2, Mux0, 0); +impl_pin!(P2_10, ADC2, Mux0, 1); +impl_pin!(P4_4, ADC2, Mux0, 2); +// impl_pin!(P2_24, ADC2, Mux0, 255); ??? +impl_pin!(P2_16, ADC2, Mux0, 4); +impl_pin!(P2_12, ADC2, Mux0, 5); +impl_pin!(P2_20, ADC2, Mux0, 6); +impl_pin!(P2_7, ADC2, Mux0, 7); +impl_pin!(P0_2, ADC2, Mux0, 8); +// ??? +// impl_pin!(P0_4, ADC2, Mux0, 255); +// impl_pin!(P0_5, ADC2, Mux0, 255); +// impl_pin!(P0_6, ADC2, Mux0, 255); +// impl_pin!(P0_7, ADC2, Mux0, 255); +// impl_pin!(P0_12, ADC2, Mux0, 255); +// impl_pin!(P0_13, ADC2, Mux0, 255); +// ??? +impl_pin!(P0_14, ADC2, Mux0, 14); +impl_pin!(P0_15, ADC2, Mux0, 15); +// ??? +// impl_pin!(P4_0, ADC2, Mux0, 255); +// impl_pin!(P4_1, ADC2, Mux0, 255); +// ??? +impl_pin!(P4_2, ADC2, Mux0, 18); +impl_pin!(P4_3, ADC2, Mux0, 19); +//impl_pin!(P4_4, ADC2, Mux0, 20); // Conflit with ADC2_A3 and ADC2_A20 using the same pin +impl_pin!(P4_5, ADC2, Mux0, 21); +impl_pin!(P4_6, ADC2, Mux0, 22); +impl_pin!(P4_7, ADC2, Mux0, 23); + +impl_pin!(P2_5, ADC3, Mux0, 0); +impl_pin!(P2_11, ADC3, Mux0, 1); +impl_pin!(P2_23, ADC3, Mux0, 2); +// impl_pin!(P2_25, ADC3, Mux0, 255); // ??? +impl_pin!(P2_17, ADC3, Mux0, 4); +impl_pin!(P2_13, ADC3, Mux0, 5); +impl_pin!(P2_21, ADC3, Mux0, 6); +impl_pin!(P2_7, ADC3, Mux0, 7); +// ??? +// impl_pin!(P3_2, ADC3, Mux0, 255); +// impl_pin!(P3_3, ADC3, Mux0, 255); +// impl_pin!(P3_4, ADC3, Mux0, 255); +// impl_pin!(P3_5, ADC3, Mux0, 255); +// ??? +impl_pin!(P3_6, ADC3, Mux0, 12); +impl_pin!(P3_7, ADC3, Mux0, 13); +impl_pin!(P3_12, ADC3, Mux0, 14); +impl_pin!(P3_13, ADC3, Mux0, 15); +impl_pin!(P3_14, ADC3, Mux0, 16); +impl_pin!(P3_15, ADC3, Mux0, 17); +impl_pin!(P3_20, ADC3, Mux0, 18); +impl_pin!(P3_21, ADC3, Mux0, 19); +impl_pin!(P3_22, ADC3, Mux0, 20); +// ??? +// impl_pin!(P3_23, ADC3, Mux0, 255); +// impl_pin!(P3_24, ADC3, Mux0, 255); +// impl_pin!(P3_25, ADC3, Mux0, 255); +// ??? diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 5876923a1..d2cda631c 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -2,6 +2,7 @@ #![no_main] use embassy_executor::Spawner; +use embassy_time::{Duration, Ticker}; use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; use hal::bind_interrupts; use hal::clocks::PoweredClock; @@ -9,9 +10,8 @@ use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::config::Config; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; -use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::cmdl1::Mode; use hal::pac::adc1::ctrl::CalAvgs; -use hal::pac::adc1::tctrl::Tcmd; use hal::peripherals::ADC1; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -38,7 +38,6 @@ async fn main(_spawner: Spawner) { trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, enable_conv_pause: false, conv_pause_delay: 0, - fifo_watermark: 0, power: PoweredClock::NormalEnabledDeepSleepDisabled, source: AdcClockSel::FroLfDiv, div: Div4::no_div(), @@ -47,23 +46,16 @@ async fn main(_spawner: Spawner) { adc.do_offset_calibration(); adc.do_auto_calibration(); - - let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config).unwrap(); - - let mut conv_trigger_config = adc.get_default_conv_trigger_config(); - conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; - conv_trigger_config.enable_hardware_trigger = false; - adc.set_conv_trigger_config(0, &conv_trigger_config); + adc.set_resolution(Mode::Data16Bits); defmt::info!("ADC configuration done..."); + let mut ticker = Ticker::every(Duration::from_millis(100)); loop { + ticker.next().await; match adc.read().await { Ok(value) => { - defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); + defmt::info!("ADC value: {}", value); } Err(e) => { defmt::error!("ADC read error: {:?}", e); diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index d048bb56f..5c4d5524c 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -2,13 +2,15 @@ #![no_main] use embassy_executor::Spawner; +use embassy_mcxa::adc::{ConvCommandConfig, ConvTriggerConfig}; +use embassy_time::{Duration, Ticker}; use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; use hal::clocks::PoweredClock; use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::config::Config; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; -use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::cmdl1::Mode; use hal::pac::adc1::ctrl::CalAvgs; use hal::pac::adc1::tctrl::Tcmd; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -34,7 +36,6 @@ async fn main(_spawner: Spawner) { trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, enable_conv_pause: false, conv_pause_delay: 0, - fifo_watermark: 0, power: PoweredClock::NormalEnabledDeepSleepDisabled, source: AdcClockSel::FroLfDiv, div: Div4::no_div(), @@ -44,20 +45,25 @@ async fn main(_spawner: Spawner) { adc.do_offset_calibration(); adc.do_auto_calibration(); - let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + let conv_command_config = ConvCommandConfig { + conversion_resolution_mode: Mode::Data16Bits, + ..ConvCommandConfig::default() + }; adc.set_conv_command_config(1, &conv_command_config).unwrap(); - let mut conv_trigger_config = adc.get_default_conv_trigger_config(); - conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; - conv_trigger_config.enable_hardware_trigger = false; - adc.set_conv_trigger_config(0, &conv_trigger_config); + let conv_trigger_config = ConvTriggerConfig { + target_command_id: Tcmd::ExecuteCmd1, + enable_hardware_trigger: false, + ..Default::default() + }; + adc.set_conv_trigger_config(0, &conv_trigger_config).unwrap(); defmt::info!("=== ADC configuration done... ==="); + let mut tick = Ticker::every(Duration::from_millis(100)); loop { - adc.do_software_trigger(1); + tick.next().await; + adc.do_software_trigger(1).unwrap(); let result = loop { match adc.get_conv_result() { Ok(res) => break res, -- cgit From ebe47b1856602f993a7f87669030afe1cd10dc51 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 16:03:04 +0100 Subject: Remove generic parameter from ADC --- embassy-mcxa/src/adc.rs | 129 ++++++++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index e4b24cffa..c19f02431 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -173,20 +173,29 @@ pub struct InterruptHandler { } /// ADC driver instance. -pub struct Adc<'a, I: Instance, M: ModeAdc> { - _inst: PhantomData<&'a mut I>, - _phantom: PhantomData, - index: u8, +pub struct Adc<'a, M: ModeAdc> { + _inst: PhantomData<&'a mut ()>, + mode: M, + + // The channel index of the pin used to create our ADC instance + channel_idx: u8, + + // The register block of the ADC instance + info: &'static pac::adc0::RegisterBlock, } -impl<'a, I: Instance> Adc<'a, I, Blocking> { +impl<'a> Adc<'a, Blocking> { /// Create a new blocking instance of the ADC driver. /// # Arguments /// * `_inst` - ADC peripheral instance /// * `pin` - GPIO pin to use for ADC /// * `config` - ADC configuration - pub fn new_blocking(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, config: LpadcConfig) -> Result { - Self::new_inner(_inst, pin, config) + pub fn new_blocking( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + config: LpadcConfig, + ) -> Result { + Self::new_inner(_inst, pin, config, Blocking) } /// Enable ADC interrupts. @@ -196,8 +205,7 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { /// # Arguments /// * `mask` - Bitmask of interrupt sources to enable pub fn enable_interrupt(&mut self, mask: u32) { - let adc = I::ptr(); - adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); + self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); } /// Disable ADC interrupts. @@ -207,15 +215,14 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { /// # Arguments /// * `mask` - Bitmask of interrupt sources to disable pub fn disable_interrupt(&mut self, mask: u32) { - let adc = I::ptr(); - adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); + self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); } pub fn set_fifo_watermark(&mut self, watermark: u8) -> Result<()> { if watermark > 0b111 { return Err(Error::InvalidConfig); } - I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) }); + self.info.fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) }); Ok(()) } @@ -234,8 +241,7 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { if trigger_id_mask > 0b1111 { return Err(Error::InvalidConfig); } - let adc = I::ptr(); - adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) }); + self.info.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) }); Ok(()) } @@ -271,8 +277,7 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { /// /// Clears all pending conversion results from the FIFO. pub fn do_reset_fifo(&self) { - let adc = I::ptr(); - adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); + self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); } /// Get conversion result from FIFO. @@ -288,7 +293,7 @@ impl<'a, I: Instance> Adc<'a, I, Blocking> { } } -impl<'a, I: Instance> Adc<'a, I, Async> { +impl<'a> Adc<'a, Async> { /// Initialize ADC with interrupt support. /// /// # Arguments @@ -296,13 +301,13 @@ impl<'a, I: Instance> Adc<'a, I, Async> { /// * `pin` - GPIO pin to use for ADC /// * `_irq` - Interrupt binding for this ADC instance /// * `config` - ADC configuration - pub fn new_async( + pub fn new_async( _inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin>, _irq: impl crate::interrupt::typelevel::Binding> + 'a, config: LpadcConfig, ) -> Result { - let adc = Self::new_inner(_inst, pin, config)?; + let adc = Self::new_inner(_inst, pin, config, Async { waiter: I::wait_cell() })?; I::Interrupt::unpend(); unsafe { I::Interrupt::enable() }; @@ -343,24 +348,26 @@ impl<'a, I: Instance> Adc<'a, I, Async> { pub fn set_averages(&mut self, avgs: Avgs) { // TODO: we should probably return a result or wait for idle? // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." - I::ptr().cmdh1().modify(|_r, w| w.avgs().variant(avgs)); + self.info.cmdh1().modify(|_r, w| w.avgs().variant(avgs)); } /// Set the sample time pub fn set_sample_time(&mut self, st: Sts) { // TODO: we should probably return a result or wait for idle? // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." - I::ptr().cmdh1().modify(|_r, w| w.sts().variant(st)); + self.info.cmdh1().modify(|_r, w| w.sts().variant(st)); } pub fn set_resolution(&mut self, mode: Mode) { // TODO: we should probably return a result or wait for idle? // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." - I::ptr().cmdl1().modify(|_r, w| w.mode().variant(mode)); + self.info.cmdl1().modify(|_r, w| w.mode().variant(mode)); } - fn wait_idle(&mut self) -> impl Future> + use<'_, I> { - I::wait_cell().wait_for(|| I::ptr().ie().read().fwmie0().bit_is_clear()) + fn wait_idle(&mut self) -> impl Future> + use<'_> { + self.mode + .waiter + .wait_for(|| self.info.ie().read().fwmie0().bit_is_clear()) } /// Read ADC value asynchronously. @@ -376,18 +383,16 @@ impl<'a, I: Instance> Adc<'a, I, Async> { /// # Returns /// 16-bit ADC conversion value pub async fn read(&mut self) -> Result { - let adc = I::ptr(); - // If we cancelled a previous read, we might still be busy, wait // until the interrupt is cleared (done by the interrupt) _ = self.wait_idle().await; // Clear the fifo - adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); + self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); // Trigger a new conversion - adc.ie().modify(|_r, w| w.fwmie0().set_bit()); - adc.swtrig().write(|w| w.swt0().set_bit()); + self.info.ie().modify(|_r, w| w.fwmie0().set_bit()); + self.info.swtrig().write(|w| w.swt0().set_bit()); // Wait for completion _ = self.wait_idle().await; @@ -396,9 +401,14 @@ impl<'a, I: Instance> Adc<'a, I, Async> { } } -impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { +impl<'a, M: ModeAdc> Adc<'a, M> { /// Internal initialization function shared by `new_async` and `new_blocking`. - fn new_inner>(_inst: Peri<'a, I>, pin: Peri<'a, P>, config: LpadcConfig) -> Result { + fn new_inner>( + _inst: Peri<'a, I>, + pin: Peri<'a, P>, + config: LpadcConfig, + mode: M, + ) -> Result { let adc = I::ptr(); _ = unsafe { @@ -488,21 +498,22 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { Ok(Self { _inst: PhantomData, - _phantom: PhantomData, - index: P::CHANNEL, + mode, + channel_idx: P::CHANNEL, + info: adc, }) } /// Perform offset calibration. /// Waits for calibration to complete before returning. pub fn do_offset_calibration(&self) { - let adc = I::ptr(); // Enable calibration mode - adc.ctrl() + self.info + .ctrl() .modify(|_, w| w.calofs().offset_calibration_request_pending()); // Wait for calibration to complete (polling status register) - while adc.stat().read().cal_rdy().is_not_set() {} + while self.info.stat().read().cal_rdy().is_not_set() {} } /// Calculate gain conversion result from gain adjustment factor. @@ -532,12 +543,13 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// Perform automatic gain calibration. pub fn do_auto_calibration(&self) { - let adc = I::ptr(); - adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); + self.info + .ctrl() + .modify(|_, w| w.cal_req().calibration_request_pending()); - while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} + while self.info.gcc0().read().rdy().is_gain_cal_not_valid() {} - let mut gcca = adc.gcc0().read().gain_cal().bits() as u32; + let mut gcca = self.info.gcc0().read().gain_cal().bits() as u32; if gcca & 0x8000 != 0 { gcca |= !0xFFFF; } @@ -545,31 +557,31 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { let gcra = 131072.0 / (131072.0 - gcca as f32); // Write to GCR0 - adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); + self.info + .gcr0() + .write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); - adc.gcr0().modify(|_, w| w.rdy().set_bit()); + self.info.gcr0().modify(|_, w| w.rdy().set_bit()); // Wait for calibration to complete (polling status register) - while adc.stat().read().cal_rdy().is_not_set() {} + while self.info.stat().read().cal_rdy().is_not_set() {} } fn set_conv_command_config_inner(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { - let adc = I::ptr(); - let (cmdl, cmdh) = match index { - 1 => (adc.cmdl1(), adc.cmdh1()), - 2 => (adc.cmdl2(), adc.cmdh2()), - 3 => (adc.cmdl3(), adc.cmdh3()), - 4 => (adc.cmdl4(), adc.cmdh4()), - 5 => (adc.cmdl5(), adc.cmdh5()), - 6 => (adc.cmdl6(), adc.cmdh6()), - 7 => (adc.cmdl7(), adc.cmdh7()), + 1 => (self.info.cmdl1(), self.info.cmdh1()), + 2 => (self.info.cmdl2(), self.info.cmdh2()), + 3 => (self.info.cmdl3(), self.info.cmdh3()), + 4 => (self.info.cmdl4(), self.info.cmdh4()), + 5 => (self.info.cmdl5(), self.info.cmdh5()), + 6 => (self.info.cmdl6(), self.info.cmdh6()), + 7 => (self.info.cmdl7(), self.info.cmdh7()), _ => return Err(Error::InvalidConfig), }; cmdl.write(|w| { unsafe { - w.adch().bits(self.index); + w.adch().bits(self.channel_idx); } w.mode().variant(config.conversion_resolution_mode) }); @@ -591,14 +603,12 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { } fn set_conv_trigger_config_inner(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { - let adc = I::ptr(); - // 0..4 are valid if trigger_id >= 4 { return Err(Error::InvalidConfig); } - let tctrl = &adc.tctrl(trigger_id); + let tctrl = &self.info.tctrl(trigger_id); tctrl.write(|w| { w.tcmd().variant(config.target_command_id); @@ -625,8 +635,7 @@ impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { /// - `Some(ConvResult)` if a result is available /// - `Err(Error::FifoEmpty)` if the FIFO is empty fn get_conv_result_inner(&self) -> Result { - let adc = I::ptr(); - let fifo = adc.resfifo0().read(); + let fifo = self.info.resfifo0().read(); if !fifo.valid().is_valid() { return Err(Error::FifoEmpty); } @@ -709,7 +718,9 @@ impl sealed::Sealed for Blocking {} impl ModeAdc for Blocking {} /// Async mode. -pub struct Async; +pub struct Async { + waiter: &'static WaitCell, +} impl sealed::Sealed for Async {} impl ModeAdc for Async {} -- cgit From 0e64db6bb72252ceb35725298bc8858d7949ba88 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 16:07:59 +0100 Subject: Remove some stale todos --- embassy-mcxa/src/adc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index c19f02431..9efac6217 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -316,12 +316,12 @@ impl<'a> Adc<'a, Async> { chained_next_command_number: Next::NoNextCmdTerminateOnFinish, enable_auto_channel_increment: false, loop_count: 0, - hardware_average_mode: Avgs::NoAverage, // todo: good config? - sample_time_mode: Sts::Sample3p5, // todo: good config? + hardware_average_mode: Avgs::NoAverage, + sample_time_mode: Sts::Sample3p5, hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, hardware_compare_value_high: 0, hardware_compare_value_low: 0, - conversion_resolution_mode: Mode::Data16Bits, // todo: good config? + conversion_resolution_mode: Mode::Data16Bits, enable_wait_trigger: false, }; -- cgit From 3bc913b77572e91e38709071f7f7bb8777847ccf Mon Sep 17 00:00:00 2001 From: Pietro Lorefice Date: Thu, 11 Dec 2025 16:13:07 +0100 Subject: stm32/spi: make SS signal polarity configurable on supported hardware --- embassy-stm32/src/spi/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c90e0cef4..af51b79b4 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -64,6 +64,16 @@ pub enum Direction { Receive, } +/// Slave Select (SS) pin polarity. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SlaveSelectPolarity { + /// SS active high + ActiveHigh, + /// SS active low + ActiveLow, +} + /// SPI configuration. #[non_exhaustive] #[derive(Copy, Clone)] @@ -86,6 +96,9 @@ pub struct Config { /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled. /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high. pub nss_output_disable: bool, + /// Slave Select (SS) pin polarity. + #[cfg(any(spi_v4, spi_v5, spi_v6))] + pub nss_polarity: SlaveSelectPolarity, } impl Default for Config { @@ -97,6 +110,8 @@ impl Default for Config { miso_pull: Pull::None, gpio_speed: Speed::VeryHigh, nss_output_disable: false, + #[cfg(any(spi_v4, spi_v5, spi_v6))] + nss_polarity: SlaveSelectPolarity::ActiveHigh, } } } @@ -123,6 +138,14 @@ impl Config { } } + #[cfg(any(spi_v4, spi_v5, spi_v6))] + fn raw_nss_polarity(&self) -> vals::Ssiop { + match self.nss_polarity { + SlaveSelectPolarity::ActiveHigh => vals::Ssiop::ACTIVE_HIGH, + SlaveSelectPolarity::ActiveLow => vals::Ssiop::ACTIVE_LOW, + } + } + #[cfg(gpio_v1)] fn sck_af(&self) -> AfType { AfType::output(OutputType::PushPull, self.gpio_speed) @@ -305,6 +328,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { #[cfg(any(spi_v4, spi_v5, spi_v6))] { let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; + let ssiop = config.raw_nss_polarity(); regs.ifcr().write(|w| w.0 = 0xffff_ffff); regs.cfg2().modify(|w| { w.set_ssoe(ssoe); @@ -318,7 +342,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { w.set_midi(0); w.set_mssi(0); w.set_afcntr(true); - w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); + w.set_ssiop(ssiop); }); regs.cfg1().modify(|w| { w.set_crcen(false); @@ -366,6 +390,8 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { #[cfg(any(spi_v4, spi_v5, spi_v6))] { + let ssiop = config.raw_nss_polarity(); + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); @@ -374,6 +400,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { w.set_cpha(cpha); w.set_cpol(cpol); w.set_lsbfirst(lsbfirst); + w.set_ssiop(ssiop); }); self.info.regs.cfg1().modify(|w| { w.set_mbr(br); @@ -446,6 +473,13 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled let nss_output_disable = !ssoe || cfg.ssm(); + #[cfg(any(spi_v4, spi_v5, spi_v6))] + let nss_polarity = if cfg.ssiop() == vals::Ssiop::ACTIVE_LOW { + SlaveSelectPolarity::ActiveLow + } else { + SlaveSelectPolarity::ActiveHigh + }; + Config { mode: Mode { polarity, phase }, bit_order, @@ -453,6 +487,8 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { miso_pull, gpio_speed: self.gpio_speed, nss_output_disable, + #[cfg(any(spi_v4, spi_v5, spi_v6))] + nss_polarity, } } @@ -769,7 +805,7 @@ impl<'d> Spi<'d, Async, Master> { ) } - /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode + /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode #[cfg(any(spi_v1, spi_v2, spi_v3))] pub fn new_bidi( peri: Peri<'d, T>, -- cgit From 91f0b599835d5bc4fc99276668d3faa0a871c38a Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 16:13:12 +0100 Subject: Remove instance generic from RTC --- embassy-mcxa/src/rtc.rs | 78 +++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs index f975d9c9f..41dcc98df 100644 --- a/embassy-mcxa/src/rtc.rs +++ b/embassy-mcxa/src/rtc.rs @@ -9,8 +9,6 @@ use crate::interrupt::typelevel::{Handler, Interrupt}; use crate::pac; use crate::pac::rtc0::cr::Um; -type Regs = pac::rtc0::RegisterBlock; - /// Global wait cell for alarm notifications static WAKER: WaitCell = WaitCell::new(); @@ -22,7 +20,7 @@ pub struct InterruptHandler { /// Trait for RTC peripheral instances pub trait Instance: PeripheralType { type Interrupt: Interrupt; - fn ptr() -> *const Regs; + fn ptr() -> &'static pac::rtc0::RegisterBlock; } /// Token for RTC0 @@ -30,8 +28,8 @@ pub type Rtc0 = crate::peripherals::RTC0; impl Instance for crate::peripherals::RTC0 { type Interrupt = crate::interrupt::typelevel::RTC; #[inline(always)] - fn ptr() -> *const Regs { - pac::Rtc0::ptr() + fn ptr() -> &'static pac::rtc0::RegisterBlock { + unsafe { &*pac::Rtc0::ptr() } } } @@ -204,18 +202,19 @@ pub fn get_default_config() -> RtcConfig { } } /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) -pub struct Rtc<'a, I: Instance> { - _inst: core::marker::PhantomData<&'a mut I>, +pub struct Rtc<'a> { + _inst: core::marker::PhantomData<&'a ()>, + info: &'static pac::rtc0::RegisterBlock, } -impl<'a, I: Instance> Rtc<'a, I> { +impl<'a> Rtc<'a> { /// Create a new instance of the real time clock. - pub fn new( + pub fn new( _inst: Peri<'a, I>, _irq: impl crate::interrupt::typelevel::Binding> + 'a, config: RtcConfig, ) -> Self { - let rtc = unsafe { &*I::ptr() }; + let info = I::ptr(); // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock // on the vsys domain is active @@ -227,13 +226,13 @@ impl<'a, I: Instance> Rtc<'a, I> { } // RTC reset - rtc.cr().modify(|_, w| w.swr().set_bit()); - rtc.cr().modify(|_, w| w.swr().clear_bit()); - rtc.tsr().write(|w| unsafe { w.bits(1) }); + info.cr().modify(|_, w| w.swr().set_bit()); + info.cr().modify(|_, w| w.swr().clear_bit()); + info.tsr().write(|w| unsafe { w.bits(1) }); - rtc.cr().modify(|_, w| w.um().variant(config.update_mode)); + info.cr().modify(|_, w| w.um().variant(config.update_mode)); - rtc.tcr().modify(|_, w| unsafe { + info.tcr().modify(|_, w| unsafe { w.cir() .bits(config.compensation_interval) .tcr() @@ -246,6 +245,7 @@ impl<'a, I: Instance> Rtc<'a, I> { Self { _inst: core::marker::PhantomData, + info, } } @@ -259,9 +259,8 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// The datetime is converted to Unix timestamp and written to the time seconds register. pub fn set_datetime(&self, datetime: RtcDateTime) { - let rtc = unsafe { &*I::ptr() }; let seconds = convert_datetime_to_seconds(&datetime); - rtc.tsr().write(|w| unsafe { w.bits(seconds) }); + self.info.tsr().write(|w| unsafe { w.bits(seconds) }); } /// Get the current date and time @@ -274,8 +273,7 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// Reads the current Unix timestamp from the time seconds register and converts it. pub fn get_datetime(&self) -> RtcDateTime { - let rtc = unsafe { &*I::ptr() }; - let seconds = rtc.tsr().read().bits(); + let seconds = self.info.tsr().read().bits(); convert_seconds_to_datetime(seconds) } @@ -295,19 +293,18 @@ impl<'a, I: Instance> Rtc<'a, I> { /// - Uses timeouts to prevent infinite loops /// - Enables the alarm interrupt after setting pub fn set_alarm(&self, alarm: RtcDateTime) { - let rtc = unsafe { &*I::ptr() }; let seconds = convert_datetime_to_seconds(&alarm); - rtc.tar().write(|w| unsafe { w.bits(0) }); + self.info.tar().write(|w| unsafe { w.bits(0) }); let mut timeout = 10000; - while rtc.tar().read().bits() != 0 && timeout > 0 { + while self.info.tar().read().bits() != 0 && timeout > 0 { timeout -= 1; } - rtc.tar().write(|w| unsafe { w.bits(seconds) }); + self.info.tar().write(|w| unsafe { w.bits(seconds) }); let mut timeout = 10000; - while rtc.tar().read().bits() != seconds && timeout > 0 { + while self.info.tar().read().bits() != seconds && timeout > 0 { timeout -= 1; } @@ -324,8 +321,7 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// Reads the alarm timestamp from the time alarm register and converts it. pub fn get_alarm(&self) -> RtcDateTime { - let rtc = unsafe { &*I::ptr() }; - let alarm_seconds = rtc.tar().read().bits(); + let alarm_seconds = self.info.tar().read().bits(); convert_seconds_to_datetime(alarm_seconds) } @@ -335,8 +331,7 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// Sets the Time Counter Enable (TCE) bit in the status register. pub fn start(&self) { - let rtc = unsafe { &*I::ptr() }; - rtc.sr().modify(|_, w| w.tce().set_bit()); + self.info.sr().modify(|_, w| w.tce().set_bit()); } /// Stop the RTC time counter @@ -345,8 +340,7 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// Clears the Time Counter Enable (TCE) bit in the status register. pub fn stop(&self) { - let rtc = unsafe { &*I::ptr() }; - rtc.sr().modify(|_, w| w.tce().clear_bit()); + self.info.sr().modify(|_, w| w.tce().clear_bit()); } /// Enable specific RTC interrupts @@ -364,19 +358,17 @@ impl<'a, I: Instance> Rtc<'a, I> { /// - Alarm Interrupt /// - Seconds Interrupt pub fn set_interrupt(&self, mask: u32) { - let rtc = unsafe { &*I::ptr() }; - if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.tiie().tiie_1()); + self.info.ier().modify(|_, w| w.tiie().tiie_1()); } if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.toie().toie_1()); + self.info.ier().modify(|_, w| w.toie().toie_1()); } if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.taie().taie_1()); + self.info.ier().modify(|_, w| w.taie().taie_1()); } if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.tsie().tsie_1()); + self.info.ier().modify(|_, w| w.tsie().tsie_1()); } } @@ -390,19 +382,17 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// This function disables the specified interrupt types. pub fn disable_interrupt(&self, mask: u32) { - let rtc = unsafe { &*I::ptr() }; - if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.tiie().tiie_0()); + self.info.ier().modify(|_, w| w.tiie().tiie_0()); } if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.toie().toie_0()); + self.info.ier().modify(|_, w| w.toie().toie_0()); } if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.taie().taie_0()); + self.info.ier().modify(|_, w| w.taie().taie_0()); } if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { - rtc.ier().modify(|_, w| w.tsie().tsie_0()); + self.info.ier().modify(|_, w| w.tsie().tsie_0()); } } @@ -412,8 +402,7 @@ impl<'a, I: Instance> Rtc<'a, I> { /// /// This function clears the Time Alarm Interrupt Enable bit. pub fn clear_alarm_flag(&self) { - let rtc = unsafe { &*I::ptr() }; - rtc.ier().modify(|_, w| w.taie().clear_bit()); + self.info.ier().modify(|_, w| w.taie().clear_bit()); } /// Wait for an RTC alarm to trigger. @@ -421,6 +410,7 @@ impl<'a, I: Instance> Rtc<'a, I> { /// # Arguments /// /// * `alarm` - The date and time when the alarm should trigger + /// /// This function will wait until the RTC alarm is triggered. /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) { -- cgit From c4855d254fdfa3f06583b0883f603fee0615bd41 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 16:19:50 +0100 Subject: Remove some unusual top level re-exports --- embassy-mcxa/src/i2c/controller.rs | 2 +- embassy-mcxa/src/lib.rs | 3 +-- embassy-mcxa/src/lpuart/mod.rs | 4 ++-- examples/mcxa/src/bin/clkout.rs | 3 +-- examples/mcxa/src/bin/i2c-scan-blocking.rs | 3 +-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs index c27d508b0..62789f85f 100644 --- a/embassy-mcxa/src/i2c/controller.rs +++ b/embassy-mcxa/src/i2c/controller.rs @@ -8,9 +8,9 @@ use embassy_hal_internal::drop::OnDrop; use mcxa_pac::lpi2c0::mtdr::Cmd; use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin}; -use crate::AnyPin; use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig}; use crate::clocks::{PoweredClock, enable_and_reset}; +use crate::gpio::AnyPin; use crate::interrupt::typelevel::Interrupt; /// Bus speed (nominal SCL, no clock stretching) diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index be279e509..25791b164 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -19,6 +19,7 @@ pub mod lpuart; pub mod ostimer; pub mod rtc; +use crate::interrupt::InterruptExt; pub use crate::pac::NVIC_PRIO_BITS; #[rustfmt::skip] @@ -337,8 +338,6 @@ embassy_hal_internal::peripherals!( // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. // Re-export interrupt traits and types -pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; -pub use interrupt::InterruptExt; #[cfg(feature = "unstable-pac")] pub use mcxa_pac as pac; #[cfg(not(feature = "unstable-pac"))] diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs index e59ce8140..bce3986b5 100644 --- a/embassy-mcxa/src/lpuart/mod.rs +++ b/embassy-mcxa/src/lpuart/mod.rs @@ -6,12 +6,12 @@ use paste::paste; use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; -use crate::gpio::SealedPin; +use crate::gpio::{AnyPin, SealedPin}; use crate::pac::lpuart0::baud::Sbns as StopBits; use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity}; use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; use crate::pac::lpuart0::stat::Msbf as MsbFirst; -use crate::{AnyPin, interrupt, pac}; +use crate::{interrupt, pac}; pub mod buffered; diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs index bfd963540..1e52912d3 100644 --- a/examples/mcxa/src/bin/clkout.rs +++ b/examples/mcxa/src/bin/clkout.rs @@ -4,8 +4,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; use embassy_mcxa::clocks::PoweredClock; -use embassy_mcxa::gpio::{DriveStrength, SlewRate}; -use embassy_mcxa::{Level, Output}; +use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; use embassy_time::Timer; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs index 0197f9b1d..bd706d712 100644 --- a/examples/mcxa/src/bin/i2c-scan-blocking.rs +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs @@ -2,8 +2,7 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa::Input; -use embassy_mcxa::gpio::Pull; +use embassy_mcxa::gpio::{Input, Pull}; use embassy_time::Timer; use hal::clocks::config::Div8; use hal::config::Config; -- cgit From 5eae94057016407c3ed40316bffae9c8efa56247 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 16:28:29 +0100 Subject: Take mut lifetime --- embassy-mcxa/src/rtc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs index 41dcc98df..c5474d34a 100644 --- a/embassy-mcxa/src/rtc.rs +++ b/embassy-mcxa/src/rtc.rs @@ -203,7 +203,7 @@ pub fn get_default_config() -> RtcConfig { } /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) pub struct Rtc<'a> { - _inst: core::marker::PhantomData<&'a ()>, + _inst: core::marker::PhantomData<&'a mut ()>, info: &'static pac::rtc0::RegisterBlock, } -- cgit From 272eaefc4279c4914d9792d197e427d61fdd5a2a Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 Dec 2025 09:53:22 -0600 Subject: rm usart_dma --- ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index cee761500..4f78f70cf 100755 --- a/ci.sh +++ b/ci.sh @@ -67,6 +67,8 @@ rm out/tests/rpi-pico/pwm rm out/tests/rpi-pico/cyw43-perf rm out/tests/rpi-pico/uart_buffered +rm out/tests/stm32h563zi/usart_dma + # tests are implemented but the HIL test farm doesn't actually have these boards, yet rm -rf out/tests/stm32c071rb rm -rf out/tests/stm32f100rd -- cgit From 0ab1e8f9faa149e14e3348f9a88fb4efea26962a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 11 Dec 2025 08:15:07 -0800 Subject: Remove critical section --- embassy-mcxa/src/reset_reason.rs | 80 ++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/embassy-mcxa/src/reset_reason.rs b/embassy-mcxa/src/reset_reason.rs index 1f5a0ec1f..f9a9ce096 100644 --- a/embassy-mcxa/src/reset_reason.rs +++ b/embassy-mcxa/src/reset_reason.rs @@ -7,47 +7,45 @@ /// Reads the most recent reset reason from the Core Mode Controller /// (CMC). pub fn reset_reason() -> ResetReason { - critical_section::with(|_| { - let regs = unsafe { &*crate::pac::Cmc::steal() }; - - let srs = regs.srs().read(); - - if srs.wakeup().is_enabled() { - ResetReason::WakeUp - } else if srs.por().bit_is_set() { - ResetReason::Por - } else if srs.vd().bit_is_set() { - ResetReason::VoltageDetect - } else if srs.warm().bit_is_set() { - ResetReason::Warm - } else if srs.fatal().bit_is_set() { - ResetReason::Fatal - } else if srs.pin().bit_is_set() { - ResetReason::Pin - } else if srs.dap().bit_is_set() { - ResetReason::Dap - } else if srs.rstack().bit_is_set() { - ResetReason::ResetAckTimeout - } else if srs.lpack().bit_is_set() { - ResetReason::LowPowerAckTimeout - } else if srs.scg().bit_is_set() { - ResetReason::SystemClockGeneration - } else if srs.wwdt0().bit_is_set() { - ResetReason::Wwdt0 - } else if srs.sw().bit_is_set() { - ResetReason::Software - } else if srs.lockup().bit_is_set() { - ResetReason::Lockup - } else if srs.cdog0().bit_is_set() { - ResetReason::Cdog0 - } else if srs.cdog1().bit_is_set() { - ResetReason::Cdog1 - } else if srs.jtag().bit_is_set() { - ResetReason::Jtag - } else { - ResetReason::Tamper - } - }) + let regs = unsafe { &*crate::pac::Cmc::steal() }; + + let srs = regs.srs().read(); + + if srs.wakeup().is_enabled() { + ResetReason::WakeUp + } else if srs.por().bit_is_set() { + ResetReason::Por + } else if srs.vd().bit_is_set() { + ResetReason::VoltageDetect + } else if srs.warm().bit_is_set() { + ResetReason::Warm + } else if srs.fatal().bit_is_set() { + ResetReason::Fatal + } else if srs.pin().bit_is_set() { + ResetReason::Pin + } else if srs.dap().bit_is_set() { + ResetReason::Dap + } else if srs.rstack().bit_is_set() { + ResetReason::ResetAckTimeout + } else if srs.lpack().bit_is_set() { + ResetReason::LowPowerAckTimeout + } else if srs.scg().bit_is_set() { + ResetReason::SystemClockGeneration + } else if srs.wwdt0().bit_is_set() { + ResetReason::Wwdt0 + } else if srs.sw().bit_is_set() { + ResetReason::Software + } else if srs.lockup().bit_is_set() { + ResetReason::Lockup + } else if srs.cdog0().bit_is_set() { + ResetReason::Cdog0 + } else if srs.cdog1().bit_is_set() { + ResetReason::Cdog1 + } else if srs.jtag().bit_is_set() { + ResetReason::Jtag + } else { + ResetReason::Tamper + } } /// Indicates the type and source of the most recent reset. -- cgit From 5c09b36a7ffbab31c97230baf0de39920e12d175 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 11 Dec 2025 17:44:52 +0100 Subject: Suggestion from review --- embassy-mcxa/src/adc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 9efac6217..f88bb6b37 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -439,9 +439,7 @@ impl<'a, M: ModeAdc> Adc<'a, M> { .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); adc.cfg().write(|w| unsafe { - if config.enable_analog_preliminary { - w.pwren().pre_enabled(); - } + w.pwren().bit(config.enable_analog_preliminary); w.pudly() .bits(config.power_up_delay) -- cgit