From 790940a18c093fb7c7f94585be81d7d02892b4fb Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 27 Nov 2025 11:29:24 -0600 Subject: stm32: use typelevel timer type --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/fmt.rs | 10 ++ embassy-stm32/src/timer/complementary_pwm.rs | 18 +-- embassy-stm32/src/timer/input_capture.rs | 2 +- embassy-stm32/src/timer/low_level.rs | 191 ++++++++++----------------- embassy-stm32/src/timer/mod.rs | 30 +++-- embassy-stm32/src/timer/one_pulse.rs | 12 +- embassy-stm32/src/timer/pwm_input.rs | 14 +- embassy-stm32/src/timer/ringbuffered.rs | 12 +- embassy-stm32/src/timer/simple_pwm.rs | 16 +-- 10 files changed, 138 insertions(+), 168 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 4c38b0add..13c95b60a 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- change: stm32: use typelevel timer type to allow dma for 32 bit timers - fix: fix incorrect handling of split interrupts in timer driver - feat: allow granular stop for regular usart - feat: Add continuous waveform method to SimplePWM diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index b6ae24ee8..b731796f0 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs @@ -206,6 +206,16 @@ macro_rules! error { }; } +#[cfg(feature = "defmt")] +trait_set::trait_set! { + pub trait Debuggable = Debug + defmt::Format; +} + +#[cfg(not(feature = "defmt"))] +trait_set::trait_set! { + pub trait Debuggable = Debug; +} + #[cfg(feature = "defmt")] #[collapse_debuginfo(yes)] macro_rules! unwrap { diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 77f19a37b..d194899c6 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -178,9 +178,9 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn get_max_duty(&self) -> u16 { if self.inner.get_counting_mode().is_center_aligned() { - self.inner.get_max_compare_value() as u16 + unwrap!(self.inner.get_max_compare_value().try_into()) } else { - self.inner.get_max_compare_value() as u16 + 1 + unwrap!(self.inner.get_max_compare_value().try_into()) + 1 } } @@ -189,7 +189,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty as _) + self.inner.set_compare_value(channel, duty.into()) } /// Set the output polarity for a given channel. @@ -220,7 +220,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { self.inner.enable_channel(channel, true); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; @@ -261,7 +261,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[u16], + duty: &[T::Word], ) { self.inner.enable_update_dma(true); self.inner @@ -291,20 +291,20 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) as u16 + unwrap!(self.inner.get_compare_value(channel).try_into()) } fn get_max_duty(&self) -> Self::Duty { if self.inner.get_counting_mode().is_center_aligned() { - self.inner.get_max_compare_value() as u16 + unwrap!(self.inner.get_max_compare_value().try_into()) } else { - self.inner.get_max_compare_value() as u16 + 1 + unwrap!(self.inner.get_max_compare_value().try_into()) + 1 } } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty as u32) + self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } fn set_period

(&mut self, period: P) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 9cf0f8c34..3e1482b67 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -97,7 +97,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { /// Get capture value for a channel. pub fn get_capture_value(&self, channel: Channel) -> u32 { - self.inner.get_capture_value(channel) + self.inner.get_capture_value(channel).into() } /// Get input interrupt. diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index aba08081f..73a81bff1 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -268,6 +268,11 @@ impl<'d, T: CoreInstance> Timer<'d, T> { unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } } + #[cfg(stm32l0)] + fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } + } + /// Start the timer. pub fn start(&self) { self.regs_core().cr1().modify(|r| r.set_cen(true)); @@ -296,7 +301,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// get the capability of the timer pub fn bits(&self) -> TimerBits { - T::BITS + match T::Word::bits() { + 16 => TimerBits::Bits16, + #[cfg(not(stm32l0))] + 32 => TimerBits::Bits32, + _ => unreachable!(), + } } /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. @@ -306,18 +316,10 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved /// because it needs to count up and down. pub fn set_frequency(&self, frequency: Hertz) { - match T::BITS { - TimerBits::Bits16 => { - self.set_frequency_internal(frequency, 16); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - self.set_frequency_internal(frequency, 32); - } - } + self.set_frequency_internal(frequency, T::Word::bits()); } - pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { + pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: usize) { let f = frequency.0; assert!(f > 0); let timer_f = T::frequency().0; @@ -326,25 +328,15 @@ impl<'d, T: CoreInstance> Timer<'d, T> { let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); - match T::BITS { - TimerBits::Bits16 => { - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr = unwrap!(u16::try_from(divide_by - 1)); - - let regs = self.regs_core(); - regs.psc().write_value(psc); - regs.arr().write(|r| r.set_arr(arr)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); + // the timer counts `0..=arr`, we want it to count `0..divide_by` + let arr: T::Word = unwrap!(T::Word::try_from(divide_by - 1)); - let regs = self.regs_gp32_unchecked(); - regs.psc().write_value(psc); - regs.arr().write_value(arr); - } - } + let regs = self.regs_gp32_unchecked(); + regs.psc().write_value(psc); + #[cfg(stm32l0)] + regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); + #[cfg(not(stm32l0))] + regs.arr().write_value(arr.into()); } /// Set tick frequency. @@ -393,23 +385,14 @@ impl<'d, T: CoreInstance> Timer<'d, T> { pub fn get_frequency(&self) -> Hertz { let timer_f = T::frequency(); - match T::BITS { - TimerBits::Bits16 => { - let regs = self.regs_core(); - let arr = regs.arr().read().arr(); - let psc = regs.psc().read(); + let regs = self.regs_gp32_unchecked(); + #[cfg(not(stm32l0))] + let arr = regs.arr().read(); + #[cfg(stm32l0)] + let arr = regs.arr().read().arr(); + let psc = regs.psc().read(); - timer_f / arr / (psc + 1) - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - let regs = self.regs_gp32_unchecked(); - let arr = regs.arr().read(); - let psc = regs.psc().read(); - - timer_f / arr / (psc + 1) - } - } + timer_f / arr / (psc + 1) } /// Get the clock frequency of the timer (before prescaler is applied). @@ -469,42 +452,29 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { } /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - pub fn get_max_compare_value(&self) -> u32 { - match T::BITS { - TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, - #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), - } + pub fn get_max_compare_value(&self) -> T::Word { + #[cfg(not(stm32l0))] + return unwrap!(self.regs_gp32_unchecked().arr().read().try_into()); + #[cfg(stm32l0)] + return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into()); } /// Set the max compare value. /// /// An update event is generated to load the new value. The update event is /// generated such that it will not cause an interrupt or DMA request. - pub fn set_max_compare_value(&self, ticks: u32) { - match T::BITS { - TimerBits::Bits16 => { - let arr = unwrap!(u16::try_from(ticks)); + pub fn set_max_compare_value(&self, ticks: T::Word) { + let arr = ticks; - let regs = self.regs_1ch(); - regs.arr().write(|r| r.set_arr(arr)); + let regs = self.regs_gp32_unchecked(); + #[cfg(not(stm32l0))] + regs.arr().write_value(arr.into()); + #[cfg(stm32l0)] + regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - let arr = ticks; - - let regs = self.regs_gp32_unchecked(); - regs.arr().write_value(arr); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); - } - } + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); } } @@ -638,26 +608,23 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set compare value for a channel. - pub fn set_compare_value(&self, channel: Channel, value: u32) { - match T::BITS { - TimerBits::Bits16 => { - let value = unwrap!(u16::try_from(value)); - self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); - } - } + pub fn set_compare_value(&self, channel: Channel, value: T::Word) { + #[cfg(not(stm32l0))] + self.regs_gp32_unchecked() + .ccr(channel.index()) + .write_value(value.into()); + #[cfg(stm32l0)] + self.regs_gp16() + .ccr(channel.index()) + .modify(|w| w.set_ccr(unwrap!(value.try_into()))); } /// Get compare value for a channel. - pub fn get_compare_value(&self, channel: Channel) -> u32 { - match T::BITS { - TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, - #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), - } + pub fn get_compare_value(&self, channel: Channel) -> T::Word { + #[cfg(not(stm32l0))] + return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into()); + #[cfg(stm32l0)] + return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); } /// Setup a ring buffer for the channel @@ -665,8 +632,8 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - dma_buf: &'a mut [u16], - ) -> WritableRingBuffer<'a, u16> { + dma_buf: &'a mut [T::Word], + ) -> WritableRingBuffer<'a, T::Word> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -686,7 +653,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { WritableRingBuffer::new( dma, req, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut T::Word, dma_buf, dma_transfer_option, ) @@ -701,7 +668,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - duty: &'a [u16], + duty: &'a [T::Word], ) -> Transfer<'a> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -719,29 +686,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { ..Default::default() }; - match self.bits() { - TimerBits::Bits16 => Transfer::new_write( - dma, - req, - duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ), - #[cfg(not(any(stm32l0)))] - TimerBits::Bits32 => { - #[cfg(not(any(bdma, gpdma)))] - panic!("unsupported timer bits"); - - #[cfg(any(bdma, gpdma))] - Transfer::new_write( - dma, - req, - duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, - dma_transfer_option, - ) - } - } + Transfer::new_write( + dma, + req, + duty, + self.regs_gp16().ccr(channel.index()).as_ptr() as *mut T::Word, + dma_transfer_option, + ) } } @@ -779,7 +730,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma: Peri<'a, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &'a [u16], + duty: &'a [T::Word], ) -> Transfer<'a> { let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; let start_ch_index = starting_channel.index(); @@ -815,14 +766,14 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_gp16().dmar().as_ptr() as *mut u16, + self.regs_gp16().dmar().as_ptr() as *mut T::Word, dma_transfer_option, ) } } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: Channel) -> u32 { + pub fn get_capture_value(&self, channel: Channel) -> T::Word { self.get_compare_value(channel) } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 3fa363881..74d1c9e77 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -15,6 +15,8 @@ pub mod qei; pub mod ringbuffered; pub mod simple_pwm; +use crate::dma::word::Word; +use crate::fmt::Debuggable; use crate::interrupt; use crate::rcc::RccPeripheral; @@ -163,7 +165,12 @@ pub trait CoreInstance: SealedInstance + 'static { type UpdateInterrupt: interrupt::typelevel::Interrupt; /// Amount of bits this timer has. - const BITS: TimerBits; + type Word: Word + + TryInto + + From + + TryFrom + + Into + + TryFrom; /// Registers for this timer. /// @@ -241,7 +248,7 @@ dma_trait!(Dma, GeneralInstance4Channel, TimerChannel); #[allow(unused)] macro_rules! impl_core_timer { - ($inst:ident, $bits:expr) => { + ($inst:ident, $bits:ident) => { impl SealedInstance for crate::peripherals::$inst { fn state() -> &'static State { static STATE: State = State::new(); @@ -251,8 +258,7 @@ macro_rules! impl_core_timer { impl CoreInstance for crate::peripherals::$inst { type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; - - const BITS: TimerBits = $bits; + type Word = $bits; fn regs() -> *mut () { crate::pac::$inst.as_ptr() @@ -306,13 +312,13 @@ macro_rules! impl_general_4ch_blank_sealed { foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -322,7 +328,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -332,7 +338,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -342,7 +348,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits32); + impl_core_timer!($inst, u32); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -353,7 +359,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -366,7 +372,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -379,7 +385,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index fe8681356..989e1d630 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -199,7 +199,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { self.inner.set_counting_mode(counting_mode); self.inner.set_tick_freq(freq); - self.inner.set_max_compare_value(pulse_end); + self.inner.set_max_compare_value(unwrap!(pulse_end.try_into())); self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); // Required for advanced timers, see GeneralInstance4Channel for details self.inner.enable_outputs(); @@ -211,14 +211,14 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// Get the end of the pulse in ticks from the trigger. pub fn pulse_end(&self) -> u32 { - let max = self.inner.get_max_compare_value(); + let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u32::MAX); max + 1 } /// Set the end of the pulse in ticks from the trigger. pub fn set_pulse_end(&mut self, ticks: u32) { - self.inner.set_max_compare_value(ticks) + self.inner.set_max_compare_value(unwrap!(ticks.try_into())) } /// Reset the timer on each trigger @@ -327,7 +327,7 @@ pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { /// Get the end of the pulse in ticks from the trigger. pub fn pulse_end(&self) -> u32 { - let max = self.inner.get_max_compare_value(); + let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u32::MAX); max + 1 } @@ -339,13 +339,13 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { /// Get the start of the pulse in ticks from the trigger. pub fn pulse_delay(&mut self) -> u32 { - self.inner.get_compare_value(self.channel) + self.inner.get_compare_value(self.channel).into() } /// Set the start of the pulse in ticks from the trigger. pub fn set_pulse_delay(&mut self, delay: u32) { assert!(delay <= self.pulse_end()); - self.inner.set_compare_value(self.channel, delay); + self.inner.set_compare_value(self.channel, unwrap!(delay.try_into())); } /// Set the pulse width in ticks. diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 057ab011a..f2f00927d 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -91,16 +91,18 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Get the period tick count pub fn get_period_ticks(&self) -> u32 { - self.inner.get_capture_value(self.channel) + self.inner.get_capture_value(self.channel).into() } /// Get the pulse width tick count pub fn get_width_ticks(&self) -> u32 { - self.inner.get_capture_value(match self.channel { - Channel::Ch1 => Channel::Ch2, - Channel::Ch2 => Channel::Ch1, - _ => panic!("Invalid channel for PWM input"), - }) + self.inner + .get_capture_value(match self.channel { + Channel::Ch1 => Channel::Ch2, + Channel::Ch2 => Channel::Ch1, + _ => panic!("Invalid channel for PWM input"), + }) + .into() } /// Get the duty cycle in 100% diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index e8f97bf59..f5a4328d1 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -25,7 +25,7 @@ use crate::dma::ringbuffer::Error; /// ``` pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { timer: ManuallyDrop>, - ring_buf: WritableRingBuffer<'d, u16>, + ring_buf: WritableRingBuffer<'d, T::Word>, channel: Channel, } @@ -33,7 +33,7 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { pub(crate) fn new( timer: ManuallyDrop>, channel: Channel, - ring_buf: WritableRingBuffer<'d, u16>, + ring_buf: WritableRingBuffer<'d, T::Word>, ) -> Self { Self { timer, @@ -55,18 +55,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { } /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. - pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + pub fn write_immediate(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { self.ring_buf.write_immediate(buf) } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + pub fn write(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { self.ring_buf.write(buf) } /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, buffer: &[u16]) -> Result { + pub async fn write_exact(&mut self, buffer: &[T::Word]) -> Result { self.ring_buf.write_exact(buffer).await } @@ -140,7 +140,7 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - let max = self.timer.get_max_compare_value(); + let max: u32 = self.timer.get_max_compare_value().into(); assert!(max < u16::MAX as u32); max as u16 + 1 } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 484e9fd81..8dc84cdfa 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -99,7 +99,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - let max = self.timer.get_max_compare_value(); + let max: u32 = self.timer.get_max_compare_value().into(); assert!(max < u16::MAX as u32); max as u16 + 1 } @@ -174,7 +174,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { pub fn into_ring_buffered_channel( mut self, tx_dma: Peri<'d, impl super::UpDma>, - dma_buf: &'d mut [u16], + dma_buf: &'d mut [T::Word], ) -> RingBufferedPwmChannel<'d, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); @@ -333,7 +333,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - let max = self.inner.get_max_compare_value(); + let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u16::MAX as u32); max as u16 + 1 } @@ -344,7 +344,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { self.inner.enable_channel(channel, true); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; @@ -385,7 +385,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[u16], + duty: &[T::Word], ) { self.inner.enable_update_dma(true); self.inner @@ -448,16 +448,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) + self.inner.get_compare_value(channel).into() } fn get_max_duty(&self) -> Self::Duty { - self.inner.get_max_compare_value() + 1 + self.inner.get_max_compare_value().into() + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { assert!(duty <= self.max_duty_cycle() as u32); - self.inner.set_compare_value(channel, duty) + self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } fn set_period

(&mut self, period: P) -- cgit From edb14f8c0966e1d22f396cbd631e5835a9a5104b Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 27 Nov 2025 20:19:06 -0600 Subject: stm32/timer: enable channels for waveform_up multi --- embassy-stm32/src/timer/complementary_pwm.rs | 5 +++++ embassy-stm32/src/timer/simple_pwm.rs | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index d194899c6..996759060 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -263,6 +263,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { ending_channel: Channel, duty: &[T::Word], ) { + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .filter(|ch| ch.index() >= starting_channel.index()) + .filter(|ch| ch.index() <= ending_channel.index()) + .for_each(|ch| self.inner.enable_channel(*ch, true)); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 8dc84cdfa..466c56db2 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -387,6 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { ending_channel: Channel, duty: &[T::Word], ) { + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .filter(|ch| ch.index() >= starting_channel.index()) + .filter(|ch| ch.index() <= ending_channel.index()) + .for_each(|ch| self.inner.enable_channel(*ch, true)); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) -- cgit From c6d6538df6ac32a17edac8f8a1e817c276f03c4c Mon Sep 17 00:00:00 2001 From: Riccardo Arena Date: Sun, 30 Nov 2025 18:06:41 +0100 Subject: fix: stm32l47*/stm32l48* adc analog pin setup --- embassy-stm32/src/adc/mod.rs | 12 ++++++------ embassy-stm32/src/gpio.rs | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 6d53d9b91..a55b99e6a 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -77,7 +77,7 @@ trait SealedInstance { } pub(crate) trait SealedAdcChannel { - #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] fn setup(&mut self) {} #[allow(unused)] @@ -185,11 +185,11 @@ pub enum RegularConversionMode { impl<'d, T: AnyInstance> Adc<'d, T> { #[cfg(any( - adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 + adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v3, adc_v4, adc_wba, adc_c0 ))] /// Read an ADC pin. pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: T::SampleTime) -> u16 { - #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] channel.setup(); // Ensure no conversions are ongoing @@ -418,7 +418,7 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri pub trait AdcChannel: SealedAdcChannel + Sized { #[allow(unused_mut)] fn degrade_adc(mut self) -> AnyAdcChannel { - #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] self.setup(); AnyAdcChannel { @@ -554,7 +554,7 @@ macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::adc::AdcChannel for crate::Peri<'_, crate::peripherals::$pin> {} impl crate::adc::SealedAdcChannel for crate::Peri<'_, crate::peripherals::$pin> { - #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] fn setup(&mut self) { ::set_as_analog(self); } @@ -582,7 +582,7 @@ macro_rules! impl_adc_pair { crate::Peri<'_, crate::peripherals::$npin>, ) { - #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] fn setup(&mut self) { ::set_as_analog(&mut self.0); ::set_as_analog(&mut self.1); diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index e7d4e9ad3..5de8bad2c 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -684,7 +684,11 @@ fn set_as_analog(pin_port: PinNumber) { }); #[cfg(gpio_v2)] - r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG)); + { + #[cfg(any(stm32l47x, stm32l48x))] + r.ascr().modify(|w| w.set_asc(n, true)); + r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG)); + } } #[inline(never)] -- cgit From 20a0f1b7dccd75908bf5f4eb6d1a6f2340926215 Mon Sep 17 00:00:00 2001 From: Riccardo Arena Date: Sun, 30 Nov 2025 18:12:42 +0100 Subject: chore: update changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) (limited to 'embassy-stm32') diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 2a99d0a96..867f83065 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 +- fix: stm32l47*/stm32l48* adc analog pin setup ## 0.4.0 - 2025-08-26 -- cgit From b466ba29d2857815e5b25af7d8ab82d5b7e05e30 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 Dec 2025 08:39:32 -0600 Subject: timer: allow 16 bit dma buffers for 32 bit timers. --- embassy-stm32/src/timer/complementary_pwm.rs | 18 ++++++++++++++---- embassy-stm32/src/timer/low_level.rs | 20 ++++++++++---------- embassy-stm32/src/timer/ringbuffered.rs | 27 ++++++++------------------- embassy-stm32/src/timer/simple_pwm.rs | 24 +++++++++++++++++------- 4 files changed, 49 insertions(+), 40 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 996759060..b9434d37b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -8,6 +8,7 @@ use super::low_level::{CountingMode, OutputPolarity, Timer}; use super::simple_pwm::PwmPin; use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; use crate::Peri; +use crate::dma::word::Word; use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::timer::TimerChannel; @@ -220,8 +221,14 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { + pub async fn waveform_up>( + &mut self, + dma: Peri<'_, impl super::UpDma>, + channel: Channel, + duty: &[W], + ) { self.inner.enable_channel(channel, true); + self.inner.set_compare_value(channel, 0.into()); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -256,18 +263,21 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub async fn waveform_up_multi_channel( + pub async fn waveform_up_multi_channel>( &mut self, dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[T::Word], + duty: &[W], ) { [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() .filter(|ch| ch.index() >= starting_channel.index()) .filter(|ch| ch.index() <= ending_channel.index()) - .for_each(|ch| self.inner.enable_channel(*ch, true)); + .for_each(|ch| { + self.inner.enable_channel(*ch, true); + self.inner.set_compare_value(*ch, 0.into()); + }); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 73a81bff1..1af66aec1 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -628,12 +628,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Setup a ring buffer for the channel - pub fn setup_ring_buffer<'a>( + pub fn setup_ring_buffer<'a, W: Word + Into>( &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - dma_buf: &'a mut [T::Word], - ) -> WritableRingBuffer<'a, T::Word> { + dma_buf: &'a mut [W], + ) -> WritableRingBuffer<'a, W> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -653,7 +653,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { WritableRingBuffer::new( dma, req, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut T::Word, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W, dma_buf, dma_transfer_option, ) @@ -664,11 +664,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub fn setup_update_dma<'a>( + pub fn setup_update_dma<'a, W: Word + Into>( &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - duty: &'a [T::Word], + duty: &'a [W], ) -> Transfer<'a> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -690,7 +690,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_gp16().ccr(channel.index()).as_ptr() as *mut T::Word, + self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, dma_transfer_option, ) } @@ -725,12 +725,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub fn setup_update_dma_burst<'a>( + pub fn setup_update_dma_burst<'a, W: Word + Into>( &mut self, dma: Peri<'a, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &'a [T::Word], + duty: &'a [W], ) -> Transfer<'a> { let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; let start_ch_index = starting_channel.index(); @@ -766,7 +766,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_gp16().dmar().as_ptr() as *mut T::Word, + self.regs_gp16().dmar().as_ptr() as *mut W, dma_transfer_option, ) } diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index f5a4328d1..fbb6b19ea 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -7,6 +7,7 @@ use super::low_level::Timer; use super::{Channel, GeneralInstance4Channel}; use crate::dma::WritableRingBuffer; use crate::dma::ringbuffer::Error; +use crate::dma::word::Word; /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. /// @@ -23,17 +24,17 @@ use crate::dma::ringbuffer::Error; /// channel.start(); // Start DMA transfer /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles /// ``` -pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { +pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into> { timer: ManuallyDrop>, - ring_buf: WritableRingBuffer<'d, T::Word>, + ring_buf: WritableRingBuffer<'d, W>, channel: Channel, } -impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { +impl<'d, T: GeneralInstance4Channel, W: Word + Into> RingBufferedPwmChannel<'d, T, W> { pub(crate) fn new( timer: ManuallyDrop>, channel: Channel, - ring_buf: WritableRingBuffer<'d, T::Word>, + ring_buf: WritableRingBuffer<'d, W>, ) -> Self { Self { timer, @@ -55,18 +56,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { } /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. - pub fn write_immediate(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ring_buf.write_immediate(buf) } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn write(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ring_buf.write(buf) } /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, buffer: &[T::Word]) -> Result { + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { self.ring_buf.write_exact(buffer).await } @@ -155,15 +156,3 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { self.timer.set_output_compare_mode(self.channel, mode); } } - -/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. -pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { - /// Channel 1 - pub ch1: RingBufferedPwmChannel<'d, T>, - /// Channel 2 - pub ch2: RingBufferedPwmChannel<'d, T>, - /// Channel 3 - pub ch3: RingBufferedPwmChannel<'d, T>, - /// Channel 4 - pub ch4: RingBufferedPwmChannel<'d, T>, -} diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 466c56db2..3f37ffcd9 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -7,6 +7,7 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::Peri; +use crate::dma::word::Word; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; @@ -171,11 +172,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// # Panics /// Panics if `dma_buf` is empty or longer than 65535 elements. - pub fn into_ring_buffered_channel( + pub fn into_ring_buffered_channel>( mut self, tx_dma: Peri<'d, impl super::UpDma>, - dma_buf: &'d mut [T::Word], - ) -> RingBufferedPwmChannel<'d, T> { + dma_buf: &'d mut [W], + ) -> RingBufferedPwmChannel<'d, T, W> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); self.timer.enable_update_dma(true); @@ -344,8 +345,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { + pub async fn waveform_up>( + &mut self, + dma: Peri<'_, impl super::UpDma>, + channel: Channel, + duty: &[W], + ) { self.inner.enable_channel(channel, true); + self.inner.set_compare_value(channel, 0.into()); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -380,18 +387,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub async fn waveform_up_multi_channel( + pub async fn waveform_up_multi_channel>( &mut self, dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[T::Word], + duty: &[W], ) { [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() .filter(|ch| ch.index() >= starting_channel.index()) .filter(|ch| ch.index() <= ending_channel.index()) - .for_each(|ch| self.inner.enable_channel(*ch, true)); + .for_each(|ch| { + self.inner.enable_channel(*ch, true); + self.inner.set_compare_value(*ch, 0.into()); + }); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) -- cgit From a1236b13c3d5fad687ae931a041ae463ac0612bb Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 Dec 2025 09:03:10 -0600 Subject: timer: clamp compare value before dma --- embassy-stm32/src/dma/word.rs | 4 ++++ embassy-stm32/src/timer/complementary_pwm.rs | 4 ++-- embassy-stm32/src/timer/low_level.rs | 12 ++++++++++++ embassy-stm32/src/timer/simple_pwm.rs | 5 +++-- 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs index fb1bde860..5c3bb8f7f 100644 --- a/embassy-stm32/src/dma/word.rs +++ b/embassy-stm32/src/dma/word.rs @@ -31,6 +31,10 @@ pub trait Word: SealedWord + Default + Copy + 'static { fn size() -> WordSize; /// Amount of bits of this word size. fn bits() -> usize; + /// Maximum value of this type. + fn max() -> usize { + (1 << Self::bits()) - 1 + } } macro_rules! impl_word { diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index b9434d37b..4d04af685 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -228,7 +228,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { duty: &[W], ) { self.inner.enable_channel(channel, true); - self.inner.set_compare_value(channel, 0.into()); + self.inner.clamp_compare_value::(channel); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -276,7 +276,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { .filter(|ch| ch.index() <= ending_channel.index()) .for_each(|ch| { self.inner.enable_channel(*ch, true); - self.inner.set_compare_value(*ch, 0.into()); + self.inner.clamp_compare_value::(*ch); }); self.inner.enable_update_dma(true); self.inner diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 1af66aec1..6a70d2a40 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -627,6 +627,18 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); } + pub(crate) fn clamp_compare_value(&mut self, channel: Channel) { + self.set_compare_value( + channel, + unwrap!( + self.get_compare_value(channel) + .into() + .clamp(0, W::max() as u32) + .try_into() + ), + ); + } + /// Setup a ring buffer for the channel pub fn setup_ring_buffer<'a, W: Word + Into>( &mut self, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 3f37ffcd9..f4b84670f 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -179,6 +179,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { ) -> RingBufferedPwmChannel<'d, T, W> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + self.timer.clamp_compare_value::(self.channel); self.timer.enable_update_dma(true); RingBufferedPwmChannel::new( @@ -352,7 +353,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { duty: &[W], ) { self.inner.enable_channel(channel, true); - self.inner.set_compare_value(channel, 0.into()); + self.inner.clamp_compare_value::(channel); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -400,7 +401,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { .filter(|ch| ch.index() <= ending_channel.index()) .for_each(|ch| { self.inner.enable_channel(*ch, true); - self.inner.set_compare_value(*ch, 0.into()); + self.inner.clamp_compare_value::(*ch); }); self.inner.enable_update_dma(true); self.inner -- cgit From 970138277bdbf176a3a9320c9d0de9256945d21e Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 Dec 2025 09:44:07 -0600 Subject: timer: use u32 for high-level api --- embassy-stm32/src/timer/complementary_pwm.rs | 12 ++++++------ embassy-stm32/src/timer/simple_pwm.rs | 28 ++++++++++++---------------- 2 files changed, 18 insertions(+), 22 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 4d04af685..6ca13820a 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -177,20 +177,20 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn get_max_duty(&self) -> u16 { + pub fn get_max_duty(&self) -> u32 { if self.inner.get_counting_mode().is_center_aligned() { - unwrap!(self.inner.get_max_compare_value().try_into()) + self.inner.get_max_compare_value().into() } else { - unwrap!(self.inner.get_max_compare_value().try_into()) + 1 + self.inner.get_max_compare_value().into() + 1 } } /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn set_duty(&mut self, channel: Channel, duty: u16) { + pub fn set_duty(&mut self, channel: Channel, duty: u32) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty.into()) + self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } /// Set the output polarity for a given channel. @@ -318,7 +318,7 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - assert!(duty <= self.get_max_duty()); + assert!(duty <= unwrap!(self.get_max_duty().try_into())); self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f4b84670f..b79ed364b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -99,18 +99,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn max_duty_cycle(&self) -> u16 { - let max: u32 = self.timer.get_max_compare_value().into(); - assert!(max < u16::MAX as u32); - max as u16 + 1 + pub fn max_duty_cycle(&self) -> u32 { + self.timer.get_max_compare_value().into() + 1 } /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. - pub fn set_duty_cycle(&mut self, duty: u16) { + pub fn set_duty_cycle(&mut self, duty: u32) { assert!(duty <= (*self).max_duty_cycle()); - self.timer.set_compare_value(self.channel, duty.into()) + self.timer.set_compare_value(self.channel, unwrap!(duty.try_into())) } /// Set the duty cycle to 0%, or always inactive. @@ -127,21 +125,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, /// and that `denom` is not zero. - pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { + pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) { assert!(denom != 0); assert!(num <= denom); let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) #[allow(clippy::cast_possible_truncation)] - self.set_duty_cycle(duty as u16); + self.set_duty_cycle(unwrap!(duty.try_into())); } /// Set the duty cycle to `percent / 100` /// /// The caller is responsible for ensuring that `percent` is less than or equal to 100. pub fn set_duty_cycle_percent(&mut self, percent: u8) { - self.set_duty_cycle_fraction(u16::from(percent), 100) + self.set_duty_cycle_fraction(percent as u32, 100) } /// Get the duty for a given channel. @@ -334,10 +332,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn max_duty_cycle(&self) -> u16 { - let max: u32 = self.inner.get_max_compare_value().into(); - assert!(max < u16::MAX as u32); - max as u16 + 1 + pub fn max_duty_cycle(&self) -> u32 { + self.inner.get_max_compare_value().into() + 1 } /// Generate a sequence of PWM waveform @@ -417,11 +413,11 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePw impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { fn max_duty_cycle(&self) -> u16 { - self.max_duty_cycle() + unwrap!(self.max_duty_cycle().try_into()) } fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { - self.set_duty_cycle(duty); + self.set_duty_cycle(duty.into()); Ok(()) } @@ -436,7 +432,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl } fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - self.set_duty_cycle_fraction(num, denom); + self.set_duty_cycle_fraction(num.into(), denom.into()); Ok(()) } -- cgit From d2ab38e3f7f0638e2fc4c6cee1ab3083986784df Mon Sep 17 00:00:00 2001 From: Elias Hanelt Date: Mon, 1 Dec 2025 14:45:05 -0800 Subject: add feature to changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) (limited to 'embassy-stm32') diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 2b273482c..5a4415fec 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -85,6 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't - feat: stm32/lcd: added implementation - change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) +- feat: stm32/spi bidirectional mode ## 0.4.0 - 2025-08-26 -- cgit From f776a10678805b8bdb5a7c675fb8e31a57e8abc0 Mon Sep 17 00:00:00 2001 From: Elias Hanelt Date: Mon, 1 Dec 2025 14:51:19 -0800 Subject: remove i2c changes from pr --- embassy-stm32/src/i2c/v2.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 6b213484c..b2ba94e21 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1600,8 +1600,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { for byte in chunk { // Wait until we have received something match self.wait_rxne(timeout) { - Ok(ReceiveResult::StopReceived) => {} - Ok(ReceiveResult::NewStart) => { + Ok(ReceiveResult::StopReceived) | Ok(ReceiveResult::NewStart) => { trace!("--- Slave RX transmission end (early)"); return Ok(total_len - remaining_len); // Return N bytes read } -- cgit From 24b675a23a3288c7f164de74540006249c00bbe9 Mon Sep 17 00:00:00 2001 From: Elias Hanelt Date: Mon, 1 Dec 2025 15:22:50 -0800 Subject: restricting bidi mode just to spi v1, v2, v3 --- embassy-stm32/src/spi/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 89553f129..0048d5d21 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -359,6 +359,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { } /// Set SPI direction + #[cfg(any(spi_v1, spi_v2, spi_v3))] pub fn set_direction(&mut self, dir: Option) { let (bidimode, bidioe) = match dir { Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT), @@ -731,7 +732,8 @@ impl<'d> Spi<'d, Async, Master> { ) } - /// Create a new SPI driver, in bidirectional 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>, sck: Peri<'d, if_afio!(impl SckPin)>, -- cgit From 678ecd58c90d256e80cc88e7d3ab5af3d9c921e3 Mon Sep 17 00:00:00 2001 From: Lutsai Aleksandr Date: Tue, 2 Dec 2025 02:31:13 +0300 Subject: fix: SSM and SSOE in SPI STM32 driver behavior, add nss_output_disable in SPI Config --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/spi/mod.rs | 51 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 7 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 70c46b025..3ad25ad33 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config - change: stm32: use typelevel timer type to allow dma for 32 bit timers - fix: fix incorrect handling of split interrupts in timer driver - feat: allow granular stop for regular usart diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index abb80ed26..c6cede85f 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -72,6 +72,10 @@ pub struct Config { /// signal rise/fall speed (slew rate) - defaults to `Medium`. /// Increase for high SPI speeds. Change to `Low` to reduce ringing. pub gpio_speed: Speed, + /// If True sets SSOE to zero even if SPI is in Master Mode. + /// 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, } impl Default for Config { @@ -82,6 +86,7 @@ impl Default for Config { frequency: Hertz(1_000_000), miso_pull: Pull::None, gpio_speed: Speed::VeryHigh, + nss_output_disable: false, } } } @@ -217,11 +222,33 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { self.info.rcc.enable_and_reset(); + /* + - Software NSS management (SSM = 1) + The slave select information is driven internally by the value of the SSI bit in the + SPI_CR1 register. The external NSS pin remains free for other application uses. + + - Hardware NSS management (SSM = 0) + Two configurations are possible depending on the NSS output configuration (SSOE bit + in register SPI_CR1). + + -- NSS output enabled (SSM = 0, SSOE = 1) + This configuration is used only when the device operates in master mode. 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) + This configuration allows multimaster capability for devices operating in master + mode. 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 + */ + let ssm = self.nss.is_none(); + let regs = self.info.regs; #[cfg(any(spi_v1, spi_v2))] { + let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable; regs.cr2().modify(|w| { - w.set_ssoe(false); + w.set_ssoe(ssoe); }); regs.cr1().modify(|w| { w.set_cpha(cpha); @@ -232,7 +259,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { w.set_spe(true); w.set_lsbfirst(lsbfirst); w.set_ssi(CM::MASTER == vals::Mstr::MASTER); - w.set_ssm(CM::MASTER == vals::Mstr::MASTER); + w.set_ssm(ssm); w.set_crcen(false); w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); // we're doing "fake rxonly", by actually writing one @@ -244,11 +271,12 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { } #[cfg(spi_v3)] { + let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable; regs.cr2().modify(|w| { let (ds, frxth) = ::CONFIG; w.set_frxth(frxth); w.set_ds(ds); - w.set_ssoe(false); + w.set_ssoe(ssoe); }); regs.cr1().modify(|w| { w.set_cpha(cpha); @@ -258,7 +286,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { w.set_br(br); w.set_lsbfirst(lsbfirst); w.set_ssi(CM::MASTER == vals::Mstr::MASTER); - w.set_ssm(CM::MASTER == vals::Mstr::MASTER); + w.set_ssm(ssm); w.set_crcen(false); w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); w.set_spe(true); @@ -266,14 +294,14 @@ 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; regs.ifcr().write(|w| w.0 = 0xffff_ffff); regs.cfg2().modify(|w| { - //w.set_ssoe(true); - w.set_ssoe(false); + w.set_ssoe(ssoe); w.set_cpha(cpha); w.set_cpol(cpol); w.set_lsbfirst(lsbfirst); - w.set_ssm(CM::MASTER == vals::Master::MASTER); + w.set_ssm(ssm); w.set_master(CM::MASTER); w.set_comm(vals::Comm::FULL_DUPLEX); w.set_ssom(vals::Ssom::ASSERTED); @@ -357,6 +385,11 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { #[cfg(any(spi_v4, spi_v5, spi_v6))] let cfg1 = self.info.regs.cfg1().read(); + #[cfg(any(spi_v1, spi_v2, spi_v3))] + let ssoe = self.info.regs.cr2().read().ssoe(); + #[cfg(any(spi_v4, spi_v5, spi_v6))] + let ssoe = cfg.ssoe(); + let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { Polarity::IdleLow } else { @@ -386,12 +419,16 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { let frequency = compute_frequency(self.kernel_clock, br); + // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled + let nss_output_disable = !ssoe || cfg.ssm(); + Config { mode: Mode { polarity, phase }, bit_order, frequency, miso_pull, gpio_speed: self.gpio_speed, + nss_output_disable, } } -- cgit From 077e59a192ca4cb096a9ef939d06ebefebbac42d Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 3 Dec 2025 09:36:58 -0600 Subject: timer: restore waveform method --- embassy-stm32/src/timer/complementary_pwm.rs | 24 ++++++++++++++++++++++-- embassy-stm32/src/timer/low_level.rs | 25 +++++++++++++++++++++---- embassy-stm32/src/timer/simple_pwm.rs | 22 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 6ca13820a..4f2ac4079 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -2,14 +2,13 @@ use core::marker::PhantomData; -pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr}; - use super::low_level::{CountingMode, OutputPolarity, Timer}; use super::simple_pwm::PwmPin; use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; use crate::Peri; use crate::dma::word::Word; use crate::gpio::{AnyPin, OutputType}; +pub use crate::pac::timer::vals::{Ccds, Ckd, Mms2, Ossi, Ossr}; use crate::time::Hertz; use crate::timer::TimerChannel; use crate::timer::low_level::OutputCompareMode; @@ -217,6 +216,27 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { self.inner.set_dead_time_value(value); } + /// Generate a sequence of PWM waveform + /// + /// Note: + /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. + /// Also be aware that embassy timers use one of timers internally. It is possible to + /// switch this timer by using `time-driver-timX` feature. + pub async fn waveform>( + &mut self, + dma: Peri<'_, impl super::Dma>, + channel: Channel, + duty: &[W], + ) { + self.inner.enable_channel(channel, true); + self.inner.enable_channel(C::CHANNEL, true); + self.inner.clamp_compare_value::(channel); + self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); + self.inner.set_cc_dma_enable_state(C::CHANNEL, true); + self.inner.setup_channel_update_dma(dma, channel, duty).await; + self.inner.set_cc_dma_enable_state(C::CHANNEL, false); + } + /// Generate a sequence of PWM waveform /// /// Note: diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 6a70d2a40..da1bfac5f 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -13,7 +13,7 @@ use embassy_hal_internal::Peri; pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; use super::*; -use crate::dma::{Transfer, WritableRingBuffer}; +use crate::dma::{self, Transfer, WritableRingBuffer}; use crate::pac::timer::vals; use crate::rcc; use crate::time::Hertz; @@ -682,9 +682,26 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { channel: Channel, duty: &'a [W], ) -> Transfer<'a> { - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); + self.setup_update_dma_inner(dma.request(), dma, channel, duty) + } + /// Generate a sequence of PWM waveform + pub fn setup_channel_update_dma<'a, C: TimerChannel, W: Word + Into>( + &mut self, + dma: Peri<'a, impl super::Dma>, + channel: Channel, + duty: &'a [W], + ) -> Transfer<'a> { + self.setup_update_dma_inner(dma.request(), dma, channel, duty) + } + + fn setup_update_dma_inner<'a, W: Word + Into>( + &mut self, + request: dma::Request, + dma: Peri<'a, impl dma::Channel>, + channel: Channel, + duty: &'a [W], + ) -> Transfer<'a> { unsafe { #[cfg(not(any(bdma, gpdma)))] use crate::dma::{Burst, FifoThreshold}; @@ -700,7 +717,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { Transfer::new_write( dma, - req, + request, duty, self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, dma_transfer_option, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index b79ed364b..3f050a366 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -11,6 +11,7 @@ use crate::dma::word::Word; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; +use crate::pac::timer::vals::Ccds; use crate::time::Hertz; /// PWM pin wrapper. @@ -336,6 +337,27 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { self.inner.get_max_compare_value().into() + 1 } + /// Generate a sequence of PWM waveform + /// + /// Note: + /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. + /// Also be aware that embassy timers use one of timers internally. It is possible to + /// switch this timer by using `time-driver-timX` feature. + pub async fn waveform>( + &mut self, + dma: Peri<'_, impl super::Dma>, + channel: Channel, + duty: &[W], + ) { + self.inner.enable_channel(channel, true); + self.inner.enable_channel(C::CHANNEL, true); + self.inner.clamp_compare_value::(channel); + self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); + self.inner.set_cc_dma_enable_state(C::CHANNEL, true); + self.inner.setup_channel_update_dma(dma, channel, duty).await; + self.inner.set_cc_dma_enable_state(C::CHANNEL, false); + } + /// Generate a sequence of PWM waveform /// /// Note: -- cgit From b732336f2f3ff9f7ec27d1d75a3e78cc1a0ad054 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 3 Dec 2025 18:04:48 +0100 Subject: GPDMA suspend channel before reset if already enabled --- embassy-stm32/src/dma/gpdma/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index bfd0570f8..51c107cb4 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs @@ -238,6 +238,11 @@ impl AnyChannel { // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); + if ch.cr().read().en() { + ch.cr().modify(|w| w.set_susp(true)); + while !ch.sr().read().suspf() {} + } + ch.cr().write(|w| w.set_reset(true)); ch.fcr().write(|w| { // Clear all irqs -- cgit From 40d00d1208c1d0ea2c9a29ea30412b0491fd0543 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 3 Dec 2025 18:21:54 +0100 Subject: Add changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) (limited to 'embassy-stm32') diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 2b9e0a89a..ac228141e 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: stm32: GPDMA driver reset ignored during channel configuration - fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config - change: stm32: use typelevel timer type to allow dma for 32 bit timers - fix: fix incorrect handling of split interrupts in timer driver -- cgit From 2691978b16cda1f0285c95f98138fb267bb11ca7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 3 Dec 2025 16:10:07 -0600 Subject: doc --- embassy-stm32/src/timer/complementary_pwm.rs | 4 +--- embassy-stm32/src/timer/low_level.rs | 3 +++ embassy-stm32/src/timer/simple_pwm.rs | 4 +--- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 4f2ac4079..19211d933 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -219,9 +219,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Generate a sequence of PWM waveform /// /// Note: - /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. - /// Also be aware that embassy timers use one of timers internally. It is possible to - /// switch this timer by using `time-driver-timX` feature. + /// The DMA channel provided does not need to correspond to the requested channel. pub async fn waveform>( &mut self, dma: Peri<'_, impl super::Dma>, diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index da1bfac5f..82e936f3a 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -686,6 +686,9 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Generate a sequence of PWM waveform + /// + /// Note: + /// The DMA channel provided does not need to correspond to the requested channel. pub fn setup_channel_update_dma<'a, C: TimerChannel, W: Word + Into>( &mut self, dma: Peri<'a, impl super::Dma>, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 3f050a366..9a5f0fd1d 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -340,9 +340,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Generate a sequence of PWM waveform /// /// Note: - /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. - /// Also be aware that embassy timers use one of timers internally. It is possible to - /// switch this timer by using `time-driver-timX` feature. + /// The DMA channel provided does not need to correspond to the requested channel. pub async fn waveform>( &mut self, dma: Peri<'_, impl super::Dma>, -- cgit From e98694606d556d611400dcaf96844343bed2c5f7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 4 Dec 2025 02:11:16 +0100 Subject: stm32/adc: add lifetime to AnyAdcChannel. --- embassy-stm32/src/adc/g4.rs | 18 +++++++++--------- embassy-stm32/src/adc/injected.rs | 10 +++++----- embassy-stm32/src/adc/mod.rs | 27 +++++++++++++++------------ 3 files changed, 29 insertions(+), 26 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 1767a3bb3..d4e526061 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -409,10 +409,10 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { /// `InjectedAdc` to enforce bounds at compile time. pub fn setup_injected_conversions<'a, const N: usize>( self, - sequence: [(AnyAdcChannel, SampleTime); N], + sequence: [(AnyAdcChannel<'a, T>, SampleTime); N], trigger: ConversionTrigger, interrupt: bool, - ) -> InjectedAdc { + ) -> InjectedAdc<'a, T, N> { assert!(N != 0, "Read sequence cannot be empty"); assert!( N <= NR_INJECTED_RANKS, @@ -424,8 +424,8 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); - for (n, (channel, sample_time)) in sequence.into_iter().enumerate() { - let sample_time = sample_time.into(); + for (n, (channel, sample_time)) in sequence.iter().enumerate() { + let sample_time = sample_time.clone().into(); if channel.channel() <= 9 { T::regs() .smpr() @@ -487,16 +487,16 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. - pub fn into_ring_buffered_and_injected<'a, const N: usize>( + pub fn into_ring_buffered_and_injected<'a, 'b, const N: usize>( self, dma: Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - regular_sequence: impl ExactSizeIterator, T::SampleTime)>, + regular_sequence: impl ExactSizeIterator, T::SampleTime)>, regular_conversion_mode: RegularConversionMode, - injected_sequence: [(AnyAdcChannel, SampleTime); N], + injected_sequence: [(AnyAdcChannel<'b, T>, SampleTime); N], injected_trigger: ConversionTrigger, injected_interrupt: bool, - ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc) { + ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<'b, T, N>) { unsafe { ( Self { @@ -531,7 +531,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { } } -impl InjectedAdc { +impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { /// Read sampled data from all injected ADC injected ranks /// Clear the JEOS flag to allow a new injected sequence pub(super) fn read_injected_data() -> [u16; N] { diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index ccaa5d1b2..1af055644 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs @@ -10,13 +10,13 @@ use crate::adc::Instance; use crate::adc::{Adc, AnyInstance}; /// Injected ADC sequence with owned channels. -pub struct InjectedAdc { - _channels: [(AnyAdcChannel, SampleTime); N], +pub struct InjectedAdc<'a, T: Instance, const N: usize> { + _channels: [(AnyAdcChannel<'a, T>, SampleTime); N], _phantom: PhantomData, } -impl InjectedAdc { - pub(crate) fn new(channels: [(AnyAdcChannel, SampleTime); N]) -> Self { +impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { + pub(crate) fn new(channels: [(AnyAdcChannel<'a, T>, SampleTime); N]) -> Self { Self { _channels: channels, _phantom: PhantomData, @@ -36,7 +36,7 @@ impl InjectedAdc { } } -impl Drop for InjectedAdc { +impl<'a, T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<'a, T, N> { fn drop(&mut self) { T::stop(); compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index a55b99e6a..9040eefe5 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -25,7 +25,8 @@ use core::marker::PhantomData; #[allow(unused)] #[cfg(not(any(adc_f3v3, adc_wba)))] pub use _version::*; -use embassy_hal_internal::{PeripheralType, impl_peripheral}; +#[allow(unused)] +use embassy_hal_internal::PeripheralType; #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] use embassy_sync::waitqueue::AtomicWaker; #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] @@ -241,10 +242,10 @@ impl<'d, T: AnyInstance> Adc<'d, T> { /// in order or require the sequence to have the same sample time for all channnels, depending /// on the number and properties of the channels in the sequence. This method will panic if /// the hardware cannot deliver the requested configuration. - pub async fn read( + pub async fn read<'a, 'b: 'a>( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, T::SampleTime)>, + sequence: impl ExactSizeIterator, T::SampleTime)>, readings: &mut [u16], ) { assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); @@ -313,11 +314,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { /// in order or require the sequence to have the same sample time for all channnels, depending /// on the number and properties of the channels in the sequence. This method will panic if /// the hardware cannot deliver the requested configuration. - pub fn into_ring_buffered<'a>( + pub fn into_ring_buffered<'a, 'b>( self, dma: embassy_hal_internal::Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, T::SampleTime)>, + sequence: impl ExactSizeIterator, T::SampleTime)>, mode: RegularConversionMode, ) -> RingBufferedAdc<'a, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); @@ -417,7 +418,10 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri #[allow(private_bounds)] pub trait AdcChannel: SealedAdcChannel + Sized { #[allow(unused_mut)] - fn degrade_adc(mut self) -> AnyAdcChannel { + fn degrade_adc<'a>(mut self) -> AnyAdcChannel<'a, T> + where + Self: 'a, + { #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] self.setup(); @@ -433,14 +437,13 @@ pub trait AdcChannel: SealedAdcChannel + Sized { /// /// This is useful in scenarios where you need the ADC channels to have the same type, such as /// storing them in an array. -pub struct AnyAdcChannel { +pub struct AnyAdcChannel<'a, T> { channel: u8, is_differential: bool, - _phantom: PhantomData, + _phantom: PhantomData<&'a mut T>, } -impl_peripheral!(AnyAdcChannel); -impl AdcChannel for AnyAdcChannel {} -impl SealedAdcChannel for AnyAdcChannel { +impl AdcChannel for AnyAdcChannel<'_, T> {} +impl SealedAdcChannel for AnyAdcChannel<'_, T> { fn channel(&self) -> u8 { self.channel } @@ -450,7 +453,7 @@ impl SealedAdcChannel for AnyAdcChannel { } } -impl AnyAdcChannel { +impl AnyAdcChannel<'_, T> { #[allow(unused)] pub fn get_hw_channel(&self) -> u8 { self.channel -- cgit From fe4e817b613475dea05ba360641378c48e3467b3 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Sat, 6 Dec 2025 18:00:15 +0200 Subject: Make adc v2 resolution public --- embassy-stm32/src/adc/v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 3c4431ae0..e18098281 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -71,7 +71,7 @@ fn from_pclk2(freq: Hertz) -> Adcpre { /// ADC configuration #[derive(Default)] pub struct AdcConfig { - resolution: Option, + pub resolution: Option, } impl super::SealedAnyInstance for T { -- cgit From 4f66b2f2090e2fcfd7d92f9ebd07cc9048eb70d7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 6 Dec 2025 12:17:00 -0600 Subject: adc: type-erase regs instance --- embassy-stm32/src/adc/adc4.rs | 207 ++++++++++++++----------------- embassy-stm32/src/adc/c0.rs | 64 +++++----- embassy-stm32/src/adc/g4.rs | 100 ++++++++------- embassy-stm32/src/adc/injected.rs | 12 +- embassy-stm32/src/adc/mod.rs | 223 ++++++++++++++++++++-------------- embassy-stm32/src/adc/ringbuffered.rs | 48 ++++---- embassy-stm32/src/adc/v2.rs | 58 ++++----- embassy-stm32/src/adc/v3.rs | 91 +++++++------- embassy-stm32/src/adc/v4.rs | 76 ++++++------ 9 files changed, 441 insertions(+), 438 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 453513309..43509873f 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -5,7 +5,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; use super::blocking_delay_us; -use crate::adc::ConversionMode; +use crate::adc::{AdcRegs, ConversionMode, Instance}; #[cfg(stm32u5)] pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; #[cfg(stm32wba)] @@ -90,135 +90,112 @@ fn from_ker_ck(frequency: Hertz) -> Presc { } } -pub trait SealedInstance { - #[allow(unused)] - fn regs() -> crate::pac::adc::Adc4; -} +impl AdcRegs for crate::pac::adc::Adc4 { + fn data(&self) -> *mut u16 { + crate::pac::adc::Adc4::dr(*self).as_ptr() as *mut u16 + } -pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { - type Interrupt: crate::interrupt::typelevel::Interrupt; -} + fn enable(&self) { + if !self.cr().read().aden() || !self.isr().read().adrdy() { + self.isr().write(|w| w.set_adrdy(true)); + self.cr().modify(|w| w.set_aden(true)); + while !self.isr().read().adrdy() {} + } + } -foreach_adc!( - (ADC4, $common_inst:ident, $clock:ident) => { - use crate::peripherals::ADC4; + fn start(&self) { + // Start conversion + self.cr().modify(|reg| { + reg.set_adstart(true); + }); + } - impl super::BasicAnyInstance for ADC4 { - type SampleTime = SampleTime; + fn stop(&self) { + let cr = self.cr().read(); + if cr.adstart() { + self.cr().modify(|w| w.set_adstp(true)); + while self.cr().read().adstart() {} } - impl super::SealedAnyInstance for ADC4 { - fn dr() -> *mut u16 { - ADC4::regs().dr().as_ptr() as *mut u16 - } + if cr.aden() || cr.adstart() { + self.cr().modify(|w| w.set_addis(true)); + while self.cr().read().aden() {} + } - fn enable() { - if !ADC4::regs().cr().read().aden() || !ADC4::regs().isr().read().adrdy() { - ADC4::regs().isr().write(|w| w.set_adrdy(true)); - ADC4::regs().cr().modify(|w| w.set_aden(true)); - while !ADC4::regs().isr().read().adrdy() {} - } - } + // Reset configuration. + self.cfgr1().modify(|reg| { + reg.set_dmaen(false); + }); + } - fn start() { - // Start conversion - ADC4::regs().cr().modify(|reg| { - reg.set_adstart(true); + fn configure_dma(&self, conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Singular => { + self.isr().modify(|reg| { + reg.set_ovr(true); + reg.set_eos(true); + reg.set_eoc(true); }); - } - fn stop() { - let cr = ADC4::regs().cr().read(); - if cr.adstart() { - ADC4::regs().cr().modify(|w| w.set_adstp(true)); - while ADC4::regs().cr().read().adstart() {} - } - - if cr.aden() || cr.adstart() { - ADC4::regs().cr().modify(|w| w.set_addis(true)); - while ADC4::regs().cr().read().aden() {} - } - - // Reset configuration. - ADC4::regs().cfgr1().modify(|reg| { - reg.set_dmaen(false); + self.cfgr1().modify(|reg| { + reg.set_dmaen(true); + reg.set_dmacfg(Dmacfg::ONE_SHOT); + #[cfg(stm32u5)] + reg.set_chselrmod(false); + #[cfg(stm32wba)] + reg.set_chselrmod(Chselrmod::ENABLE_INPUT) }); } + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] + _ => unreachable!(), + } + } - fn configure_dma(conversion_mode: ConversionMode) { - match conversion_mode { - ConversionMode::Singular => { - ADC4::regs().isr().modify(|reg| { - reg.set_ovr(true); - reg.set_eos(true); - reg.set_eoc(true); - }); - - ADC4::regs().cfgr1().modify(|reg| { - reg.set_dmaen(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); - #[cfg(stm32u5)] - reg.set_chselrmod(false); - #[cfg(stm32wba)] - reg.set_chselrmod(Chselrmod::ENABLE_INPUT) - }); - } - #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] - _ => unreachable!(), - } - } - - fn configure_sequence(sequence: impl ExactSizeIterator) { - let mut prev_channel: i16 = -1; - #[cfg(stm32wba)] - ADC4::regs().chselr().write_value(Chselr(0_u32)); - #[cfg(stm32u5)] - ADC4::regs().chselrmod0().write_value(Chselr(0_u32)); - for (_i, ((channel, _), sample_time)) in sequence.enumerate() { - ADC4::regs().smpr().modify(|w| { - w.set_smp(_i, sample_time); - }); - - let channel_num = channel; - if channel_num as i16 <= prev_channel { - return; - }; - prev_channel = channel_num as i16; - - #[cfg(stm32wba)] - ADC4::regs().chselr().modify(|w| { - w.set_chsel0(channel as usize, true); - }); - #[cfg(stm32u5)] - ADC4::regs().chselrmod0().modify(|w| { - w.set_chsel(channel as usize, true); - }); - } - } + fn configure_sequence(&self, sequence: impl ExactSizeIterator) { + let mut prev_channel: i16 = -1; + #[cfg(stm32wba)] + self.chselr().write_value(Chselr(0_u32)); + #[cfg(stm32u5)] + self.chselrmod0().write_value(Chselr(0_u32)); + for (_i, ((channel, _), sample_time)) in sequence.enumerate() { + self.smpr().modify(|w| { + w.set_smp(_i, sample_time); + }); + + let channel_num = channel; + if channel_num as i16 <= prev_channel { + return; + }; + prev_channel = channel_num as i16; - fn convert() -> u16 { - // Reset interrupts - ADC4::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); + #[cfg(stm32wba)] + self.chselr().modify(|w| { + w.set_chsel0(channel as usize, true); + }); + #[cfg(stm32u5)] + self.chselrmod0().modify(|w| { + w.set_chsel(channel as usize, true); + }); + } + } - // Start conversion - ADC4::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); + fn convert(&self) { + // Reset interrupts + self.isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); - while !ADC4::regs().isr().read().eos() { - // spin - } + // Start conversion + self.cr().modify(|reg| { + reg.set_adstart(true); + }); - ADC4::regs().dr().read().0 as u16 - } + while !self.isr().read().eos() { + // spin } - - impl super::AnyInstance for ADC4 {} - }; -); + } +} pub struct Adc4<'d, T: Instance> { #[allow(unused)] @@ -231,7 +208,7 @@ pub enum Adc4Error { DMAError, } -impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { +impl<'d, T: Instance> super::Adc<'d, T> { /// Create a new ADC driver. pub fn new_adc4(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); @@ -267,7 +244,7 @@ impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { blocking_delay_us(1); - T::enable(); + T::regs().enable(); // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 3e109e429..2f0f326af 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -4,7 +4,7 @@ use pac::adccommon::vals::Presc; use stm32_metapac::adc::vals::{SampleTime, Scandir}; use super::{Adc, Instance, Resolution, blocking_delay_us}; -use crate::adc::{AnyInstance, ConversionMode}; +use crate::adc::{AdcRegs, ConversionMode}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -43,52 +43,52 @@ fn from_ker_ck(frequency: Hertz) -> Presc { } } -impl super::SealedAnyInstance for T { - fn dr() -> *mut u16 { - T::regs().dr().as_ptr() as *mut u16 +impl AdcRegs for crate::pac::adc::Adc { + fn data(&self) -> *mut u16 { + crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 } - fn enable() { - T::regs().isr().modify(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); + fn enable(&self) { + self.isr().modify(|w| w.set_adrdy(true)); + self.cr().modify(|w| w.set_aden(true)); // ADRDY is "ADC ready". Wait until it will be True. - while !T::regs().isr().read().adrdy() {} + while !self.isr().read().adrdy() {} } - fn start() { + fn start(&self) { // Start conversion - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_adstart(true); }); } - fn stop() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { + fn stop(&self) { + if self.cr().read().adstart() && !self.cr().read().addis() { + self.cr().modify(|reg| { reg.set_adstp(Adstp::STOP); }); - while T::regs().cr().read().adstart() {} + while self.cr().read().adstart() {} } // Reset configuration. - T::regs().cfgr1().modify(|reg| { + self.cfgr1().modify(|reg| { reg.set_cont(false); reg.set_dmacfg(Dmacfg::from_bits(0)); reg.set_dmaen(false); }); } - fn configure_dma(conversion_mode: super::ConversionMode) { + fn configure_dma(&self, conversion_mode: super::ConversionMode) { match conversion_mode { ConversionMode::Singular => { // Enable overrun control, so no new DMA requests will be generated until // previous DR values is read. - T::regs().isr().modify(|reg| { + self.isr().modify(|reg| { reg.set_ovr(true); }); // Set continuous mode with oneshot dma. - T::regs().cfgr1().modify(|reg| { + self.cfgr1().modify(|reg| { reg.set_discen(false); reg.set_cont(true); reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); @@ -99,7 +99,7 @@ impl super::SealedAnyInstance for T { } } - fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(&self, sequence: impl ExactSizeIterator) { let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; let mut is_ordered_up = true; let mut is_ordered_down = true; @@ -109,7 +109,7 @@ impl super::SealedAnyInstance for T { let mut last_channel: u8 = 0; let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; - T::regs().chselr_sq().write(|w| { + self.chselr_sq().write(|w| { for (i, ((channel, _), _sample_time)) in sequence.enumerate() { assert!( sample_time == _sample_time || i == 0, @@ -146,42 +146,40 @@ impl super::SealedAnyInstance for T { ); // Set required channels for multi-convert. - unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } + unsafe { (self.chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } } - T::regs().smpr().modify(|w| { + self.smpr().modify(|w| { w.smpsel(0); w.set_smp1(sample_time); }); - T::regs().cfgr1().modify(|reg| { + self.cfgr1().modify(|reg| { reg.set_chselrmod(!needs_hw); reg.set_align(Align::RIGHT); reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK }); }); // Trigger and wait for the channel selection procedure to complete. - T::regs().isr().modify(|w| w.set_ccrdy(false)); - while !T::regs().isr().read().ccrdy() {} + self.isr().modify(|w| w.set_ccrdy(false)); + while !self.isr().read().ccrdy() {} } - fn convert() -> u16 { + fn convert(&self) { // Set single conversion mode. - T::regs().cfgr1().modify(|w| w.set_cont(false)); + self.cfgr1().modify(|w| w.set_cont(false)); // Start conversion - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_adstart(true); }); // Waiting for End Of Conversion (EOC). - while !T::regs().isr().read().eoc() {} - - T::regs().dr().read().data() as u16 + while !self.isr().read().eoc() {} } } -impl<'d, T: AnyInstance> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { rcc::enable_and_reset::(); @@ -225,7 +223,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> { T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); - T::enable(); + T::regs().enable(); // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index d4e526061..53c1ecd31 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -12,7 +12,7 @@ use super::{ Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, blocking_delay_us, }; -use crate::adc::{AnyInstance, SealedAdcChannel}; +use crate::adc::{AdcRegs, BasicAdcRegs, SealedAdcChannel}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -68,79 +68,77 @@ pub struct ConversionTrigger { pub edge: Exten, } -impl super::SealedAnyInstance for T { - fn dr() -> *mut u16 { - T::regs().dr().as_ptr() as *mut u16 +impl super::AdcRegs for crate::pac::adc::Adc { + fn data(&self) -> *mut u16 { + crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 } - fn enable() { + fn enable(&self) { // Make sure bits are off - while T::regs().cr().read().addis() { + while self.cr().read().addis() { // spin } - if !T::regs().cr().read().aden() { + if !self.cr().read().aden() { // Enable ADC - T::regs().isr().modify(|reg| { + self.isr().modify(|reg| { reg.set_adrdy(true); }); - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_aden(true); }); - while !T::regs().isr().read().adrdy() { + while !self.isr().read().adrdy() { // spin } } } - fn start() { - T::regs().cr().modify(|reg| { + fn start(&self) { + self.cr().modify(|reg| { reg.set_adstart(true); }); } - fn stop() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { + fn stop(&self) { + if self.cr().read().adstart() && !self.cr().read().addis() { + self.cr().modify(|reg| { reg.set_adstp(Adstp::STOP); }); // The software must poll ADSTART until the bit is reset before assuming the // ADC is completely stopped - while T::regs().cr().read().adstart() {} + while self.cr().read().adstart() {} } // Disable dma control and continuous conversion, if enabled - T::regs().cfgr().modify(|reg| { + self.cfgr().modify(|reg| { reg.set_cont(false); reg.set_dmaen(Dmaen::DISABLE); }); } - fn convert() -> u16 { - T::regs().isr().modify(|reg| { + fn convert(&self) { + self.isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); }); // Start conversion - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_adstart(true); }); - while !T::regs().isr().read().eos() { + while !self.isr().read().eos() { // spin } - - T::regs().dr().read().0 as u16 } - fn configure_dma(conversion_mode: ConversionMode) { - T::regs().isr().modify(|reg| { + fn configure_dma(&self, conversion_mode: ConversionMode) { + self.isr().modify(|reg| { reg.set_ovr(true); }); - T::regs().cfgr().modify(|reg| { + self.cfgr().modify(|reg| { reg.set_discen(false); // Convert all channels for each trigger reg.set_dmacfg(match conversion_mode { ConversionMode::Singular => Dmacfg::ONE_SHOT, @@ -152,43 +150,43 @@ impl super::SealedAnyInstance for T { if let ConversionMode::Repeated(mode) = conversion_mode { match mode { RegularConversionMode::Continuous => { - T::regs().cfgr().modify(|reg| { + self.cfgr().modify(|reg| { reg.set_cont(true); }); } RegularConversionMode::Triggered(trigger) => { - T::regs().cfgr().modify(|r| { + self.cfgr().modify(|r| { r.set_cont(false); // New trigger is neede for each sample to be read }); - T::regs().cfgr().modify(|r| { + self.cfgr().modify(|r| { r.set_extsel(trigger.channel); r.set_exten(trigger.edge); }); // Regular conversions uses DMA so no need to generate interrupt - T::regs().ier().modify(|r| r.set_eosie(false)); + self.ier().modify(|r| r.set_eosie(false)); } } } } - fn configure_sequence(sequence: impl ExactSizeIterator) { - T::regs().cr().modify(|w| w.set_aden(false)); + fn configure_sequence(&self, sequence: impl ExactSizeIterator) { + self.cr().modify(|w| w.set_aden(false)); // Set sequence length - T::regs().sqr1().modify(|w| { + self.sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); }); #[cfg(stm32g4)] let mut difsel = DifselReg::default(); - let mut smpr = T::regs().smpr().read(); - let mut smpr2 = T::regs().smpr2().read(); - let mut sqr1 = T::regs().sqr1().read(); - let mut sqr2 = T::regs().sqr2().read(); - let mut sqr3 = T::regs().sqr3().read(); - let mut sqr4 = T::regs().sqr4().read(); + let mut smpr = self.smpr().read(); + let mut smpr2 = self.smpr2().read(); + let mut sqr1 = self.sqr1().read(); + let mut sqr2 = self.sqr2().read(); + let mut sqr3 = self.sqr3().read(); + let mut sqr4 = self.sqr4().read(); // Configure channels and ranks for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { @@ -230,18 +228,18 @@ impl super::SealedAnyInstance for T { } } - T::regs().smpr().write_value(smpr); - T::regs().smpr2().write_value(smpr2); - T::regs().sqr1().write_value(sqr1); - T::regs().sqr2().write_value(sqr2); - T::regs().sqr3().write_value(sqr3); - T::regs().sqr4().write_value(sqr4); + self.smpr().write_value(smpr); + self.smpr2().write_value(smpr2); + self.sqr1().write_value(sqr1); + self.sqr2().write_value(sqr2); + self.sqr3().write_value(sqr3); + self.sqr4().write_value(sqr4); #[cfg(stm32g4)] - T::regs().difsel().write_value(difsel); + self.difsel().write_value(difsel); } } -impl<'d, T: Instance + AnyInstance> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); @@ -293,7 +291,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { blocking_delay_us(20); - T::enable(); + T::regs().enable(); // single conversion mode, software trigger T::regs().cfgr().modify(|w| { @@ -420,7 +418,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { NR_INJECTED_RANKS ); - T::enable(); + T::regs().enable(); T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); @@ -491,7 +489,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { self, dma: Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - regular_sequence: impl ExactSizeIterator, T::SampleTime)>, + regular_sequence: impl ExactSizeIterator, ::SampleTime)>, regular_conversion_mode: RegularConversionMode, injected_sequence: [(AnyAdcChannel<'b, T>, SampleTime); N], injected_trigger: ConversionTrigger, @@ -531,7 +529,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { } } -impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { +impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { /// Read sampled data from all injected ADC injected ranks /// Clear the JEOS flag to allow a new injected sequence pub(super) fn read_injected_data() -> [u16; N] { diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index 1af055644..029722b84 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs @@ -4,18 +4,18 @@ use core::sync::atomic::{Ordering, compiler_fence}; #[allow(unused_imports)] use embassy_hal_internal::Peri; -use super::{AnyAdcChannel, SampleTime}; +use super::{AdcRegs, AnyAdcChannel, SampleTime}; +use crate::adc::Adc; #[allow(unused_imports)] use crate::adc::Instance; -use crate::adc::{Adc, AnyInstance}; /// Injected ADC sequence with owned channels. -pub struct InjectedAdc<'a, T: Instance, const N: usize> { +pub struct InjectedAdc<'a, T: Instance, const N: usize> { _channels: [(AnyAdcChannel<'a, T>, SampleTime); N], _phantom: PhantomData, } -impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { +impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { pub(crate) fn new(channels: [(AnyAdcChannel<'a, T>, SampleTime); N]) -> Self { Self { _channels: channels, @@ -36,9 +36,9 @@ impl<'a, T: Instance, const N: usize> InjectedAdc<'a, T, N> { } } -impl<'a, T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<'a, T, N> { +impl<'a, T: Instance, const N: usize> Drop for InjectedAdc<'a, T, N> { fn drop(&mut self) { - T::stop(); + T::regs().stop(); compiler_fence(Ordering::SeqCst); } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 9040eefe5..a6af1175a 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -32,6 +32,11 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] pub use ringbuffered::RingBufferedAdc; +#[cfg(adc_u5)] +use crate::pac::adc::vals::Adc4SampleTime; +#[cfg(adc_wba)] +use crate::pac::adc::vals::SampleTime as Adc4SampleTime; + #[cfg(any(adc_u5, adc_wba))] #[path = "adc4.rs"] pub mod adc4; @@ -44,10 +49,10 @@ pub use crate::pac::adc::vals::Res as Resolution; pub use crate::pac::adc::vals::SampleTime; use crate::peripherals; -dma_trait!(RxDma, AnyInstance); +dma_trait!(RxDma, Instance); /// Analog to Digital driver. -pub struct Adc<'d, T: AnyInstance> { +pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::Peri<'d, T>, } @@ -66,10 +71,53 @@ impl State { } } -trait SealedInstance { - #[cfg(not(adc_wba))] - #[allow(unused)] +#[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] +trait_set::trait_set! { + pub trait DefaultInstance = Instance; +} + +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_g4, adc_c0))] +trait_set::trait_set! { + pub trait DefaultInstance = Instance; +} + +#[cfg(adc_wba)] +trait_set::trait_set! { + pub trait DefaultInstance = Instance; +} + +pub trait BasicAdcRegs { + type SampleTime; +} + +#[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 +))] +trait AdcRegs: BasicAdcRegs { + fn enable(&self); + fn start(&self); + fn stop(&self); + fn convert(&self); + fn configure_dma(&self, conversion_mode: ConversionMode); + fn configure_sequence(&self, sequence: impl ExactSizeIterator); + fn data(&self) -> *mut u16; +} + +#[allow(private_bounds)] +pub trait BasicInstance { + #[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 + ))] + type Regs: AdcRegs; +} + +trait SealedInstance: BasicInstance { + #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] fn regs() -> crate::pac::adc::Adc; + #[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 + ))] + fn regs() -> Self::Regs; #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))] #[allow(unused)] fn common_regs() -> crate::pac::adccommon::AdcCommon; @@ -90,56 +138,6 @@ pub(crate) trait SealedAdcChannel { } } -// Temporary patch for ADCs that have not implemented the standard iface yet -#[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] -trait_set::trait_set! { - pub trait AnyInstance = Instance; -} - -#[cfg(any( - adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 -))] -pub trait BasicAnyInstance { - type SampleTime; -} - -#[cfg(any( - adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 -))] -pub(self) trait SealedAnyInstance: BasicAnyInstance { - fn enable(); - fn start(); - fn stop(); - fn convert() -> u16; - fn configure_dma(conversion_mode: ConversionMode); - fn configure_sequence(sequence: impl ExactSizeIterator); - #[allow(dead_code)] - fn dr() -> *mut u16; -} - -// On chips without ADC4, AnyInstance is an Instance -#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))] -#[allow(private_bounds)] -pub trait AnyInstance: SealedAnyInstance + Instance {} - -// On chips with ADC4, AnyInstance is an Instance or adc4::Instance -#[cfg(any(adc_v4, adc_u5, adc_wba))] -#[allow(private_bounds)] -pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} - -// Implement AnyInstance automatically for SealedAnyInstance -#[cfg(any( - adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 -))] -impl BasicAnyInstance for T { - type SampleTime = SampleTime; -} - -#[cfg(any( - adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 -))] -impl AnyInstance for T {} - #[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] /// Number of samples used for averaging. #[derive(Copy, Clone, Debug)] @@ -184,28 +182,33 @@ pub enum RegularConversionMode { Triggered(ConversionTrigger), } -impl<'d, T: AnyInstance> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { #[cfg(any( adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v3, adc_v4, adc_wba, adc_c0 ))] /// Read an ADC pin. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: T::SampleTime) -> u16 { + pub fn blocking_read( + &mut self, + channel: &mut impl AdcChannel, + sample_time: ::SampleTime, + ) -> u16 { #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] channel.setup(); // Ensure no conversions are ongoing - T::stop(); + T::regs().stop(); #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] - T::enable(); - T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + T::regs().enable(); + T::regs().configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); // On chips with differential channels, enable after configure_sequence to allow setting differential channels // // TODO: If hardware allows, enable after configure_sequence on all chips #[cfg(any(adc_g4, adc_h5))] - T::enable(); + T::regs().enable(); + T::regs().convert(); - T::convert() + unsafe { *T::regs().data() } } #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] @@ -245,7 +248,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> { pub async fn read<'a, 'b: 'a>( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, T::SampleTime)>, + sequence: impl ExactSizeIterator, ::SampleTime)>, readings: &mut [u16], ) { assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); @@ -259,11 +262,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { ); // Ensure no conversions are ongoing - T::stop(); + T::regs().stop(); #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] - T::enable(); + T::regs().enable(); - T::configure_sequence( + T::regs().configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); @@ -271,20 +274,20 @@ impl<'d, T: AnyInstance> Adc<'d, T> { // // TODO: If hardware allows, enable after configure_sequence on all chips #[cfg(any(adc_g4, adc_h5))] - T::enable(); - T::configure_dma(ConversionMode::Singular); + T::regs().enable(); + T::regs().configure_dma(ConversionMode::Singular); let request = rx_dma.request(); let transfer = - unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; + unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::regs().data(), readings, Default::default()) }; - T::start(); + T::regs().start(); // Wait for conversion sequence to finish. transfer.await; // Ensure conversions are finished. - T::stop(); + T::regs().stop(); } #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] @@ -318,7 +321,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> { self, dma: embassy_hal_internal::Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, T::SampleTime)>, + sequence: impl ExactSizeIterator, ::SampleTime)>, mode: RegularConversionMode, ) -> RingBufferedAdc<'a, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); @@ -328,11 +331,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { "Asynchronous read sequence cannot be more than 16 in length" ); // Ensure no conversions are ongoing - T::stop(); + T::regs().stop(); #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] - T::enable(); + T::regs().enable(); - T::configure_sequence( + T::regs().configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); @@ -340,8 +343,8 @@ impl<'d, T: AnyInstance> Adc<'d, T> { // // TODO: If hardware allows, enable after configure_sequence on all chips #[cfg(any(adc_g4, adc_h5))] - T::enable(); - T::configure_dma(ConversionMode::Repeated(mode)); + T::regs().enable(); + T::regs().configure_dma(ConversionMode::Repeated(mode)); core::mem::forget(self); @@ -442,8 +445,8 @@ pub struct AnyAdcChannel<'a, T> { is_differential: bool, _phantom: PhantomData<&'a mut T>, } -impl AdcChannel for AnyAdcChannel<'_, T> {} -impl SealedAdcChannel for AnyAdcChannel<'_, T> { +impl AdcChannel for AnyAdcChannel<'_, T> {} +impl SealedAdcChannel for AnyAdcChannel<'_, T> { fn channel(&self) -> u8 { self.channel } @@ -459,16 +462,35 @@ impl AnyAdcChannel<'_, T> { self.channel } } + +#[cfg(not(adc_wba))] +impl BasicAdcRegs for crate::pac::adc::Adc { + type SampleTime = SampleTime; +} + +#[cfg(any(adc_wba, adc_u5))] +impl BasicAdcRegs for crate::pac::adc::Adc4 { + type SampleTime = Adc4SampleTime; +} + #[cfg(adc_wba)] foreach_adc!( (ADC4, $common_inst:ident, $clock:ident) => { - impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { - fn regs() -> crate::pac::adc::Adc4 { + impl crate::adc::BasicInstance for peripherals::ADC4 { + type Regs = crate::pac::adc::Adc4; + } + + impl crate::adc::SealedInstance for peripherals::ADC4 { + fn regs() -> Self::Regs { crate::pac::ADC4 } + + fn common_regs() -> crate::pac::adccommon::AdcCommon { + return crate::pac::$common_inst + } } - impl crate::adc::adc4::Instance for peripherals::ADC4 { + impl crate::adc::Instance for peripherals::ADC4 { type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; } }; @@ -493,20 +515,32 @@ foreach_adc!( #[cfg(adc_u5)] foreach_adc!( (ADC4, $common_inst:ident, $clock:ident) => { - impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { - fn regs() -> crate::pac::adc::Adc4 { + impl crate::adc::BasicInstance for peripherals::ADC4 { + type Regs = crate::pac::adc::Adc4; + } + + impl crate::adc::SealedInstance for peripherals::ADC4 { + fn regs() -> Self::Regs { crate::pac::ADC4 } + + fn common_regs() -> crate::pac::adccommon::AdcCommon { + return crate::pac::$common_inst + } } - impl crate::adc::adc4::Instance for peripherals::ADC4 { + impl crate::adc::Instance for peripherals::ADC4 { type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; } }; ($inst:ident, $common_inst:ident, $clock:ident) => { + impl crate::adc::BasicInstance for peripherals::$inst { + type Regs = crate::pac::adc::Adc; + } + impl crate::adc::SealedInstance for peripherals::$inst { - fn regs() -> crate::pac::adc::Adc { + fn regs() -> Self::Regs { crate::pac::$inst } @@ -524,14 +558,23 @@ foreach_adc!( #[cfg(not(any(adc_u5, adc_wba)))] foreach_adc!( ($inst:ident, $common_inst:ident, $clock:ident) => { + impl crate::adc::BasicInstance for peripherals::$inst { + #[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 + ))] + type Regs = crate::pac::adc::Adc; + } + impl crate::adc::SealedInstance for peripherals::$inst { - #[cfg(not(adc_wba))] - fn regs() -> crate::pac::adc::Adc { + #[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 + ))] + fn regs() -> Self::Regs { crate::pac::$inst } - #[cfg(adc_wba)] - fn regs() -> crate::pac::adc::Adc4 { + #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] + fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 5437866d3..242a1a89c 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; #[allow(unused_imports)] use embassy_hal_internal::Peri; -use crate::adc::AnyInstance; +use super::AdcRegs; #[allow(unused_imports)] use crate::adc::{Instance, RxDma}; #[allow(unused_imports)] @@ -19,7 +19,7 @@ pub struct RingBufferedAdc<'d, T: Instance> { ring_buf: ReadableRingBuffer<'d, u16>, } -impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { +impl<'d, T: Instance> RingBufferedAdc<'d, T> { pub(crate) fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { //dma side setup let opts = TransferOptions { @@ -31,8 +31,7 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { // Safety: we forget the struct before this function returns. let request = dma.request(); - let ring_buf = - unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) }; + let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, T::regs().data(), dma_buf, opts) }; Self { _phantom: PhantomData, @@ -45,7 +44,7 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { compiler_fence(Ordering::SeqCst); self.ring_buf.start(); - T::start(); + T::regs().start(); } pub fn stop(&mut self) { @@ -117,15 +116,15 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { self.start(); } - #[cfg(adc_v2)] - { - // Clear overrun flag if set. - if T::regs().sr().read().ovr() { - self.stop(); - - return Err(OverrunError); - } - } + // #[cfg(adc_v2)] + // { + // // Clear overrun flag if set. + // if T::regs().sr().read().ovr() { + // self.stop(); + // + // return Err(OverrunError); + // } + // } self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) } @@ -143,15 +142,16 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { self.start(); } - #[cfg(adc_v2)] - { - // Clear overrun flag if set. - if T::regs().sr().read().ovr() { - self.stop(); + // #[cfg(adc_v2)] + // { + // // Clear overrun flag if set. + // if T::regs().sr().read().ovr() { + // self.stop(); + // + // return Err(OverrunError); + // } + // } - return Err(OverrunError); - } - } loop { match self.ring_buf.read(buf) { Ok((0, _)) => {} @@ -168,9 +168,9 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { } } -impl Drop for RingBufferedAdc<'_, T> { +impl Drop for RingBufferedAdc<'_, T> { fn drop(&mut self) { - T::stop(); + T::regs().stop(); compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e18098281..b026383d5 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,7 +1,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; -use crate::adc::{Adc, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcRegs, Instance, Resolution, SampleTime}; use crate::pac::adc::vals; pub use crate::pac::adccommon::vals::Adcpre; use crate::time::Hertz; @@ -74,28 +74,28 @@ pub struct AdcConfig { pub resolution: Option, } -impl super::SealedAnyInstance for T { - fn dr() -> *mut u16 { - T::regs().dr().as_ptr() as *mut u16 +impl super::AdcRegs for crate::pac::adc::Adc { + fn data(&self) -> *mut u16 { + crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 } - fn enable() { - T::regs().cr2().modify(|reg| { + fn enable(&self) { + self.cr2().modify(|reg| { reg.set_adon(true); }); blocking_delay_us(3); } - fn start() { + fn start(&self) { // Begin ADC conversions - T::regs().cr2().modify(|reg| { + self.cr2().modify(|reg| { reg.set_swstart(true); }); } - fn stop() { - let r = T::regs(); + fn stop(&self) { + let r = self; // Stop ADC r.cr2().modify(|reg| { @@ -114,36 +114,34 @@ impl super::SealedAnyInstance for T { w.set_ovrie(false); }); - clear_interrupt_flags(r); + clear_interrupt_flags(*r); compiler_fence(Ordering::SeqCst); } - fn convert() -> u16 { + fn convert(&self) { // clear end of conversion flag - T::regs().sr().modify(|reg| { + self.sr().modify(|reg| { reg.set_eoc(false); }); // Start conversion - T::regs().cr2().modify(|reg| { + self.cr2().modify(|reg| { reg.set_swstart(true); }); - while T::regs().sr().read().strt() == false { + while self.sr().read().strt() == false { // spin //wait for actual start } - while T::regs().sr().read().eoc() == false { + while self.sr().read().eoc() == false { // spin //wait for finish } - - T::regs().dr().read().0 as u16 } - fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(&self, conversion_mode: ConversionMode) { match conversion_mode { ConversionMode::Repeated(_) => { - let r = T::regs(); + let r = self; // Clear all interrupts r.sr().modify(|regs| { @@ -177,25 +175,25 @@ impl super::SealedAnyInstance for T { } } - fn configure_sequence(sequence: impl ExactSizeIterator) { - T::regs().cr2().modify(|reg| { + fn configure_sequence(&self, sequence: impl ExactSizeIterator) { + self.cr2().modify(|reg| { reg.set_adon(true); }); // Check the sequence is long enough - T::regs().sqr1().modify(|r| { + self.sqr1().modify(|r| { r.set_l((sequence.len() - 1).try_into().unwrap()); }); for (i, ((ch, _), sample_time)) in sequence.enumerate() { // Set the channel in the right sequence field. - T::regs().sqr3().modify(|w| w.set_sq(i, ch)); + self.sqr3().modify(|w| w.set_sq(i, ch)); let sample_time = sample_time.into(); if ch <= 9 { - T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); + self.smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); + self.smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } @@ -203,7 +201,7 @@ impl super::SealedAnyInstance for T { impl<'d, T> Adc<'d, T> where - T: Instance + super::AnyInstance, + T: Instance, { pub fn new(adc: Peri<'d, T>) -> Self { Self::new_with_config(adc, Default::default()) @@ -214,7 +212,7 @@ where let presc = from_pclk2(T::frequency()); T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); - T::enable(); + T::regs().enable(); if let Some(resolution) = config.resolution { T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); @@ -259,9 +257,7 @@ where impl<'d, T: Instance> Drop for Adc<'d, T> { fn drop(&mut self) { - T::regs().cr2().modify(|reg| { - reg.set_adon(false); - }); + T::regs().stop(); rcc::disable::(); } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index b270588c4..9cc44aa9a 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -146,63 +146,63 @@ pub struct AdcConfig { pub averaging: Option, } -impl super::SealedAnyInstance for T { - fn dr() -> *mut u16 { - T::regs().dr().as_ptr() as *mut u16 +impl super::AdcRegs for crate::pac::adc::Adc { + fn data(&self) -> *mut u16 { + crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 } // Enable ADC only when it is not already running. - fn enable() { + fn enable(&self) { // Make sure bits are off - while T::regs().cr().read().addis() { + while self.cr().read().addis() { // spin } - if !T::regs().cr().read().aden() { + if !self.cr().read().aden() { // Enable ADC - T::regs().isr().modify(|reg| { + self.isr().modify(|reg| { reg.set_adrdy(true); }); - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_aden(true); }); - while !T::regs().isr().read().adrdy() { + while !self.isr().read().adrdy() { // spin } } } - fn start() { - T::regs().cr().modify(|reg| { + fn start(&self) { + self.cr().modify(|reg| { reg.set_adstart(true); }); } - fn stop() { + fn stop(&self) { // Ensure conversions are finished. - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { + if self.cr().read().adstart() && !self.cr().read().addis() { + self.cr().modify(|reg| { reg.set_adstp(true); }); - while T::regs().cr().read().adstart() {} + while self.cr().read().adstart() {} } // Reset configuration. #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { + self.cfgr().modify(|reg| { reg.set_cont(false); reg.set_dmaen(false); }); #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { + self.cfgr1().modify(|reg| { reg.set_cont(false); reg.set_dmaen(false); }); } /// Perform a single conversion. - fn convert() -> u16 { + fn convert(&self) { // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be // corrupted, so we discard it and measure again. @@ -216,36 +216,34 @@ impl super::SealedAnyInstance for T { let len = 1; for _ in 0..len { - T::regs().isr().modify(|reg| { + self.isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); }); // Start conversion - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_adstart(true); }); - while !T::regs().isr().read().eos() { + while !self.isr().read().eos() { // spin } } - - T::regs().dr().read().0 as u16 } - fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(&self, conversion_mode: ConversionMode) { // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. - T::regs().isr().modify(|reg| { + self.isr().modify(|reg| { reg.set_ovr(true); }); #[cfg(not(any(adc_g0, adc_u0)))] - let regs = T::regs().cfgr(); + let regs = self.cfgr(); #[cfg(any(adc_g0, adc_u0))] - let regs = T::regs().cfgr1(); + let regs = self.cfgr1(); regs.modify(|reg| { reg.set_discen(false); @@ -259,13 +257,13 @@ impl super::SealedAnyInstance for T { }); } - fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(&self, sequence: impl ExactSizeIterator) { #[cfg(adc_h5)] - T::regs().cr().modify(|w| w.set_aden(false)); + self.cr().modify(|w| w.set_aden(false)); // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().sqr1().modify(|w| { + self.sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); }); @@ -273,8 +271,8 @@ impl super::SealedAnyInstance for T { { let mut sample_times = Vec::::new(); - T::regs().chselr().write(|chselr| { - T::regs().smpr().write(|smpr| { + self.chselr().write(|chselr| { + self.smpr().write(|smpr| { for ((channel, _), sample_time) in sequence { chselr.set_chsel(channel.into(), true); if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { @@ -306,22 +304,22 @@ impl super::SealedAnyInstance for T { // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." #[cfg(any(adc_h5, adc_h7rs))] if channel == 0 { - T::regs().or().modify(|reg| reg.set_op0(true)); + self.or().modify(|reg| reg.set_op0(true)); } // Configure channel cfg_if! { if #[cfg(adc_u0)] { // On G0 and U6 all channels use the same sampling time. - T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); + self.smpr().modify(|reg| reg.set_smp1(sample_time.into())); } else if #[cfg(any(adc_h5, adc_h7rs))] { match channel { - 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), - _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), + 0..=9 => self.smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), + _ => self.smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), } } else { let sample_time = sample_time.into(); - T::regs() + self .smpr(channel as usize / 10) .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); } @@ -331,9 +329,8 @@ impl super::SealedAnyInstance for T { { use crate::pac::adc::vals::Pcsel; - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() + self.cfgr2().modify(|w| w.set_lshift(0)); + self.pcsel() .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); } @@ -341,22 +338,22 @@ impl super::SealedAnyInstance for T { #[cfg(not(any(adc_g0, adc_u0)))] match _i { 0..=3 => { - T::regs().sqr1().modify(|w| { + self.sqr1().modify(|w| { w.set_sq(_i, channel); }); } 4..=8 => { - T::regs().sqr2().modify(|w| { + self.sqr2().modify(|w| { w.set_sq(_i - 4, channel); }); } 9..=13 => { - T::regs().sqr3().modify(|w| { + self.sqr3().modify(|w| { w.set_sq(_i - 9, channel); }); } 14..=15 => { - T::regs().sqr4().modify(|w| { + self.sqr4().modify(|w| { w.set_sq(_i - 14, channel); }); } @@ -375,20 +372,20 @@ impl super::SealedAnyInstance for T { } #[cfg(adc_h5)] - T::regs().difsel().write(|w| w.set_difsel(difsel)); + self.difsel().write(|w| w.set_difsel(difsel)); // On G0 and U0 enabled channels are sampled from 0 to last channel. // It is possible to add up to 8 sequences if CHSELRMOD = 1. // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. #[cfg(adc_u0)] - T::regs().chselr().modify(|reg| { + self.chselr().modify(|reg| { reg.set_chsel(channel_mask); }); } } } -impl<'d, T: Instance> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { /// Enable the voltage regulator fn init_regulator() { rcc::enable_and_reset::(); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index a3d9e6176..09fc2ab22 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; use pac::adccommon::vals::Presc; use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; -use crate::adc::ConversionMode; +use crate::adc::{AdcRegs, ConversionMode}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -80,65 +80,63 @@ pub struct AdcConfig { pub averaging: Option, } -impl super::SealedAnyInstance for T { - fn dr() -> *mut u16 { - T::regs().dr().as_ptr() as *mut u16 +impl AdcRegs for crate::pac::adc::Adc { + fn data(&self) -> *mut u16 { + crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 } - fn enable() { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); + fn enable(&self) { + self.isr().write(|w| w.set_adrdy(true)); + self.cr().modify(|w| w.set_aden(true)); + while !self.isr().read().adrdy() {} + self.isr().write(|w| w.set_adrdy(true)); } - fn start() { + fn start(&self) { // Start conversion - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_adstart(true); }); } - fn stop() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { + fn stop(&self) { + if self.cr().read().adstart() && !self.cr().read().addis() { + self.cr().modify(|reg| { reg.set_adstp(Adstp::STOP); }); - while T::regs().cr().read().adstart() {} + while self.cr().read().adstart() {} } // Reset configuration. - T::regs().cfgr().modify(|reg| { + self.cfgr().modify(|reg| { reg.set_cont(false); reg.set_dmngt(Dmngt::from_bits(0)); }); } - fn convert() -> u16 { - T::regs().isr().modify(|reg| { + fn convert(&self) { + self.isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); }); // Start conversion - T::regs().cr().modify(|reg| { + self.cr().modify(|reg| { reg.set_adstart(true); }); - while !T::regs().isr().read().eos() { + while !self.isr().read().eos() { // spin } - - T::regs().dr().read().0 as u16 } - fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(&self, conversion_mode: ConversionMode) { match conversion_mode { ConversionMode::Singular => { - T::regs().isr().modify(|reg| { + self.isr().modify(|reg| { reg.set_ovr(true); }); - T::regs().cfgr().modify(|reg| { + self.cfgr().modify(|reg| { reg.set_cont(true); reg.set_dmngt(Dmngt::DMA_ONE_SHOT); }); @@ -148,9 +146,9 @@ impl super::SealedAnyInstance for T { } } - fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(&self, sequence: impl ExactSizeIterator) { // Set sequence length - T::regs().sqr1().modify(|w| { + self.sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); }); @@ -158,39 +156,35 @@ impl super::SealedAnyInstance for T { for (i, ((channel, _), sample_time)) in sequence.enumerate() { let sample_time = sample_time.into(); if channel <= 9 { - T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); + self.smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); } else { - T::regs() - .smpr(1) - .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); + self.smpr(1).modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); } #[cfg(any(stm32h7, stm32u5))] { - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() - .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + self.cfgr2().modify(|w| w.set_lshift(0)); + self.pcsel().modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); } match i { 0..=3 => { - T::regs().sqr1().modify(|w| { + self.sqr1().modify(|w| { w.set_sq(i, channel); }); } 4..=8 => { - T::regs().sqr2().modify(|w| { + self.sqr2().modify(|w| { w.set_sq(i - 4, channel); }); } 9..=13 => { - T::regs().sqr3().modify(|w| { + self.sqr3().modify(|w| { w.set_sq(i - 9, channel); }); } 14..=15 => { - T::regs().sqr4().modify(|w| { + self.sqr4().modify(|w| { w.set_sq(i - 14, channel); }); } @@ -200,7 +194,7 @@ impl super::SealedAnyInstance for T { } } -impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { let s = Self::new(adc); @@ -292,7 +286,7 @@ impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { blocking_delay_us(1); - T::enable(); + T::regs().enable(); // single conversion mode, software trigger T::regs().cfgr().modify(|w| { -- cgit From fe88223469ca872b9b30b32e1b3f4ad219cfebed Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 6 Dec 2025 12:31:55 -0600 Subject: adc/g4: optimize --- embassy-stm32/src/adc/g4.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 53c1ecd31..e93ed945f 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -13,6 +13,7 @@ use super::{ blocking_delay_us, }; use crate::adc::{AdcRegs, BasicAdcRegs, SealedAdcChannel}; +use crate::pac::adc::regs::{Smpr, Smpr2, Sqr1, Sqr2, Sqr3, Sqr4}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -174,19 +175,17 @@ impl super::AdcRegs for crate::pac::adc::Adc { fn configure_sequence(&self, sequence: impl ExactSizeIterator) { self.cr().modify(|w| w.set_aden(false)); - // Set sequence length - self.sqr1().modify(|w| { - w.set_l(sequence.len() as u8 - 1); - }); - #[cfg(stm32g4)] let mut difsel = DifselReg::default(); - let mut smpr = self.smpr().read(); - let mut smpr2 = self.smpr2().read(); - let mut sqr1 = self.sqr1().read(); - let mut sqr2 = self.sqr2().read(); - let mut sqr3 = self.sqr3().read(); - let mut sqr4 = self.sqr4().read(); + let mut smpr = Smpr::default(); + let mut smpr2 = Smpr2::default(); + let mut sqr1 = Sqr1::default(); + let mut sqr2 = Sqr2::default(); + let mut sqr3 = Sqr3::default(); + let mut sqr4 = Sqr4::default(); + + // Set sequence length + sqr1.set_l(sequence.len() as u8 - 1); // Configure channels and ranks for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { -- cgit From 997ad131325a30f79ef5bf407200830ce488302e Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 6 Dec 2025 15:24:30 -0600 Subject: low-power: improve debug logic --- embassy-stm32/src/exti.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/low_power.rs | 46 ++++++++++++++++++++++------------------ embassy-stm32/src/rcc/mod.rs | 2 +- embassy-stm32/src/time_driver.rs | 6 ++++++ embassy-stm32/src/timer/mod.rs | 4 ++-- 6 files changed, 36 insertions(+), 26 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 7b7896d46..458174b5d 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -74,7 +74,7 @@ unsafe fn on_irq() { } #[cfg(feature = "low-power")] - crate::low_power::Executor::on_wakeup_irq(); + crate::low_power::Executor::on_wakeup_irq_or_event(); } struct BitIter(u32); diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 6b213484c..7ad9978b1 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -73,7 +73,7 @@ pub(crate) unsafe fn on_interrupt() { // restore the clocks to their last configured state as // much is lost in STOP modes #[cfg(all(feature = "low-power", stm32wlex))] - crate::low_power::Executor::on_wakeup_irq(); + crate::low_power::Executor::on_wakeup_irq_or_event(); let regs = T::info().regs; let isr = regs.isr().read(); diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index cdf3323fb..94217f07f 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -88,7 +88,7 @@ foreach_interrupt! { #[interrupt] #[allow(non_snake_case)] unsafe fn $irq() { - Executor::on_wakeup_irq(); + Executor::on_wakeup_irq_or_event(); } }; } @@ -99,7 +99,7 @@ foreach_interrupt! { #[interrupt] #[allow(non_snake_case)] unsafe fn $irq() { - Executor::on_wakeup_irq(); + Executor::on_wakeup_irq_or_event(); } }; } @@ -164,7 +164,11 @@ impl Executor { } } - pub(crate) unsafe fn on_wakeup_irq() { + pub(crate) unsafe fn on_wakeup_irq_or_event() { + if !get_driver().is_stopped() { + return; + } + critical_section::with(|cs| { #[cfg(stm32wlex)] { @@ -172,6 +176,17 @@ impl Executor { use crate::pac::{PWR, RCC}; use crate::rcc::init as init_rcc; + let es = crate::pac::PWR.extscr().read(); + match (es.c1stopf(), es.c1stop2f()) { + (true, false) => debug!("low power: wake from STOP1"), + (false, true) => debug!("low power: wake from STOP2"), + (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"), + (false, false) => trace!("low power: stop mode not entered"), + }; + crate::pac::PWR.extscr().modify(|w| { + w.set_c1cssf(false); + }); + let extscr = PWR.extscr().read(); if extscr.c1stop2f() || extscr.c1stopf() { // when we wake from any stop mode we need to re-initialize the rcc @@ -190,7 +205,8 @@ impl Executor { } } get_driver().resume_time(cs); - trace!("low power: resume"); + + trace!("low power: resume time"); }); } @@ -201,10 +217,8 @@ impl Executor { fn stop_mode(_cs: CriticalSection) -> Option { // We cannot enter standby because we will lose program state. if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { - trace!("low power: stop 2"); Some(StopMode::Stop2) } else if unsafe { REFCOUNT_STOP1 == 0 } { - trace!("low power: stop 1"); Some(StopMode::Stop1) } else { trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { @@ -305,9 +319,11 @@ impl Executor { get_driver().pause_time(cs).ok()?; self.configure_stop(cs, stop_mode).ok()?; - Some(()) + Some(stop_mode) }) - .map(|_| { + .map(|stop_mode| { + trace!("low power: enter stop: {}", stop_mode); + #[cfg(not(feature = "low-power-debug-with-sleep"))] Self::get_scb().set_sleepdeep(); }); @@ -339,19 +355,7 @@ impl Executor { self.inner.poll(); self.configure_pwr(); asm!("wfe"); - #[cfg(stm32wlex)] - { - let es = crate::pac::PWR.extscr().read(); - match (es.c1stopf(), es.c1stop2f()) { - (true, false) => debug!("low power: wake from STOP1"), - (false, true) => debug!("low power: wake from STOP2"), - (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"), - (false, false) => trace!("low power: stop mode not entered"), - }; - crate::pac::PWR.extscr().modify(|w| { - w.set_c1cssf(false); - }); - } + Self::on_wakeup_irq_or_event(); }; } } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 1dd634cfe..5890e68f4 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -172,7 +172,7 @@ pub(crate) struct RccInfo { /// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. #[cfg(feature = "low-power")] #[allow(dead_code)] -#[derive(Debug, Clone, Copy, PartialEq, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Default, defmt::Format)] pub enum StopMode { #[default] /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index cfcf5f3fd..ed5d902bd 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -329,6 +329,12 @@ impl RtcDriver { regs_gp16().cr1().modify(|w| w.set_cen(true)); } + #[cfg(feature = "low-power")] + /// Returns whether time is currently stopped + pub(crate) fn is_stopped(&self) -> bool { + !regs_gp16().cr1().read().cen() + } + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { let r = regs_gp16(); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 74d1c9e77..998e3a6f4 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -406,7 +406,7 @@ pub struct UpdateInterruptHandler { impl interrupt::typelevel::Handler for UpdateInterruptHandler { unsafe fn on_interrupt() { #[cfg(feature = "low-power")] - crate::low_power::Executor::on_wakeup_irq(); + crate::low_power::Executor::on_wakeup_irq_or_event(); let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); @@ -436,7 +436,7 @@ impl interrupt::typelevel::Handler Date: Sat, 6 Dec 2025 16:57:18 -0600 Subject: low-power: rework stoppableperipheral traits --- embassy-stm32/src/dma/mod.rs | 40 ++++++++++++++++++---------------------- embassy-stm32/src/rcc/mod.rs | 10 +++++++++- 2 files changed, 27 insertions(+), 23 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index efb324fa6..90feab167 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -25,7 +25,7 @@ pub(crate) use util::*; pub(crate) mod ringbuffer; pub mod word; -use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; use crate::interrupt; use crate::rcc::StoppablePeripheral; @@ -47,18 +47,9 @@ pub type Request = u8; #[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] pub type Request = (); -impl<'a> StoppablePeripheral for Peri<'a, AnyChannel> { - #[cfg(feature = "low-power")] - fn stop_mode(&self) -> crate::rcc::StopMode { - self.stop_mode - } -} - -pub(crate) trait SealedChannel { +pub(crate) trait SealedChannel: StoppablePeripheral { #[cfg(not(stm32n6))] fn id(&self) -> u8; - #[cfg(feature = "low-power")] - fn stop_mode(&self) -> crate::rcc::StopMode; } #[cfg(not(stm32n6))] @@ -74,16 +65,19 @@ 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::dma::SealedChannel for crate::peripherals::$channel_peri { - fn id(&self) -> u8 { - $index - } - + impl crate::rcc::StoppablePeripheral for crate::peripherals::$channel_peri { #[cfg(feature = "low-power")] fn stop_mode(&self) -> crate::rcc::StopMode { crate::rcc::StopMode::$stop_mode } } + + impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { + fn id(&self) -> u8 { + $index + } + } + impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { unsafe fn on_irq() { crate::dma::AnyChannel { @@ -102,7 +96,7 @@ macro_rules! dma_channel_impl { Self { id: crate::dma::SealedChannel::id(&val), #[cfg(feature = "low-power")] - stop_mode: crate::dma::SealedChannel::stop_mode(&val), + stop_mode: crate::rcc::StoppablePeripheral::stop_mode(&val), } } } @@ -123,16 +117,18 @@ impl AnyChannel { } } +impl StoppablePeripheral for AnyChannel { + #[cfg(feature = "low-power")] + fn stop_mode(&self) -> crate::rcc::StopMode { + self.stop_mode + } +} + impl SealedChannel for AnyChannel { #[cfg(not(stm32n6))] fn id(&self) -> u8 { self.id } - - #[cfg(feature = "low-power")] - fn stop_mode(&self) -> crate::rcc::StopMode { - self.stop_mode - } } impl Channel for AnyChannel {} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 5890e68f4..a33ec5d7d 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -12,6 +12,7 @@ pub use bd::*; #[cfg(any(mco, mco1, mco2))] mod mco; use critical_section::CriticalSection; +use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(any(mco, mco1, mco2))] pub use mco::*; @@ -381,12 +382,19 @@ pub(crate) trait StoppablePeripheral { } #[cfg(feature = "low-power")] -impl<'a> StoppablePeripheral for StopMode { +impl StoppablePeripheral for StopMode { fn stop_mode(&self) -> StopMode { *self } } +impl<'a, T: StoppablePeripheral + PeripheralType> StoppablePeripheral for Peri<'a, T> { + #[cfg(feature = "low-power")] + fn stop_mode(&self) -> StopMode { + T::stop_mode(&self) + } +} + pub(crate) struct BusyPeripheral { peripheral: T, } -- cgit From 982f7b32180e6f88ab44cd9d385c3b3c4e37969f Mon Sep 17 00:00:00 2001 From: liebman Date: Sun, 7 Dec 2025 09:11:28 -0800 Subject: low-power: add defmt flush before WFE and cleanup RCC re-initialization on wakeup for stm32wlex --- embassy-stm32/src/low_power.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 94217f07f..2388abe3c 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -172,10 +172,6 @@ impl Executor { critical_section::with(|cs| { #[cfg(stm32wlex)] { - use crate::pac::rcc::vals::Sw; - use crate::pac::{PWR, RCC}; - use crate::rcc::init as init_rcc; - let es = crate::pac::PWR.extscr().read(); match (es.c1stopf(), es.c1stop2f()) { (true, false) => debug!("low power: wake from STOP1"), @@ -187,14 +183,11 @@ impl Executor { w.set_c1cssf(false); }); - let extscr = PWR.extscr().read(); - if extscr.c1stop2f() || extscr.c1stopf() { + if es.c1stop2f() || es.c1stopf() { // when we wake from any stop mode we need to re-initialize the rcc - while RCC.cfgr().read().sws() != Sw::MSI {} - - init_rcc(RCC_CONFIG.unwrap()); + crate::rcc::init(RCC_CONFIG.unwrap()); - if extscr.c1stop2f() { + if es.c1stop2f() { // when we wake from STOP2, we need to re-initialize the time driver get_driver().init_timer(cs); // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) @@ -354,6 +347,8 @@ impl Executor { unsafe { self.inner.poll(); self.configure_pwr(); + #[cfg(feature = "defmt")] + defmt::flush(); asm!("wfe"); Self::on_wakeup_irq_or_event(); }; -- cgit From c93da8fc6d76cd6978c0cfbfb3ab42b0285af8d8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 8 Dec 2025 08:19:24 -0600 Subject: low-power: use scoped block stop Co-authored-by: hjeldin --- embassy-stm32/src/i2c/mod.rs | 2 +- embassy-stm32/src/i2c/v1.rs | 7 +++++++ embassy-stm32/src/i2c/v2.rs | 8 ++++++++ embassy-stm32/src/qspi/mod.rs | 4 +++- embassy-stm32/src/rcc/mod.rs | 10 ++++++++++ embassy-stm32/src/sdmmc/mod.rs | 12 +++++++++++- embassy-stm32/src/spi/mod.rs | 10 +++++++++- embassy-stm32/src/usart/mod.rs | 8 ++++---- 8 files changed, 53 insertions(+), 8 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index ee60c3f44..0bf430ffc 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -226,7 +226,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { } fn enable_and_init(&mut self, config: Config) { - self.info.rcc.enable_and_reset(); + self.info.rcc.enable_and_reset_without_stop(); self.init(config); } } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 128a58db7..81a6d74c1 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -529,6 +529,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write. pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) .await?; @@ -537,6 +538,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Read. pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) .await?; @@ -701,6 +703,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); // Check empty read buffer before starting transaction. Otherwise, we would not generate the // stop condition below. if read_buffer.is_empty() { @@ -719,6 +722,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, @@ -1357,6 +1361,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// (Read/Write) and the matched address. This method will suspend until /// an address match occurs. pub async fn listen(&mut self) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting async listen for address match"); let state = self.state; let info = self.info; @@ -1421,6 +1426,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// /// Returns the number of bytes stored in the buffer (not total received). pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); if buffer.is_empty() { @@ -1454,6 +1460,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// /// Returns the total number of bytes transmitted (data + padding). pub async fn respond_to_read(&mut self, data: &[u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); if data.is_empty() { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7ad9978b1..32ce83d40 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1075,6 +1075,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write. pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if write.is_empty() { self.write_internal(address.into(), write, true, timeout) @@ -1089,6 +1090,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// The buffers are concatenated in a single write transaction. pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if write.is_empty() { @@ -1120,6 +1122,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Read. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if buffer.is_empty() { @@ -1132,6 +1135,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if write.is_empty() { @@ -1157,6 +1161,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if operations.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -1677,6 +1682,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { /// /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. pub async fn listen(&mut self) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); let state = self.state; self.info.regs.cr1().modify(|reg| { reg.set_addrie(true); @@ -1733,12 +1739,14 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// /// Returns the total number of bytes received. pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); timeout.with(self.read_dma_internal_slave(buffer, timeout)).await } /// Respond to a read request from an I2C master. pub async fn respond_to_read(&mut self, write: &[u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); timeout.with(self.write_dma_internal_slave(write, timeout)).await } diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index bb4f4f1d0..1f47f4845 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -111,7 +111,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { config: Config, fsel: FlashSelection, ) -> Self { - rcc::enable_and_reset::(); + rcc::enable_and_reset_without_stop::(); while T::REGS.sr().read().busy() {} @@ -403,6 +403,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { /// Async read data, using DMA. pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let transfer = self.start_read_transfer(transaction, buf); transfer.await; } @@ -443,6 +444,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { /// Async write data, using DMA. pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let transfer = self.start_write_transfer(transaction, buf); transfer.await; } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index a33ec5d7d..2a9a1595a 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -498,6 +498,16 @@ pub fn enable_and_reset() { T::RCC_INFO.enable_and_reset(); } +/// Enables and resets peripheral `T` without incrementing the stop refcount. +/// +/// # Safety +/// +/// Peripheral must not be in use. +// TODO: should this be `unsafe`? +pub fn enable_and_reset_without_stop() { + T::RCC_INFO.enable_and_reset_without_stop(); +} + /// Disables peripheral `T`. /// /// # Safety diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index e05131040..37ef7099f 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -675,7 +675,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { d7: Option>, config: Config, ) -> Self { - rcc::enable_and_reset::(); + rcc::enable_and_reset_without_stop::(); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -1075,6 +1075,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 card_capacity = self.card()?.get_capacity(); // NOTE(unsafe) DataBlock uses align 4 @@ -1114,6 +1115,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 card_capacity = self.card()?.get_capacity(); // NOTE(unsafe) reinterpret buffer as &mut [u32] @@ -1160,6 +1162,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 card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) DataBlock uses align 4 @@ -1216,6 +1219,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 card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) reinterpret buffer as &[u32] @@ -1542,6 +1546,8 @@ 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(); + self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await } @@ -1711,6 +1717,8 @@ 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(); + self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await } @@ -1845,6 +1853,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(); // 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) }; @@ -1862,6 +1871,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(); // 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) }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 3b39fd2fb..c90e0cef4 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -230,7 +230,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { let cpol = config.raw_polarity(); let lsbfirst = config.raw_byte_order(); - self.info.rcc.enable_and_reset(); + self.info.rcc.enable_and_reset_without_stop(); /* - Software NSS management (SSM = 1) @@ -848,6 +848,7 @@ impl<'d> Spi<'d, Async, Master> { impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// SPI write, using DMA. pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if data.is_empty() { return Ok(()); } @@ -879,6 +880,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// SPI read, using DMA. #[cfg(any(spi_v4, spi_v5, spi_v6))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if data.is_empty() { return Ok(()); } @@ -966,6 +968,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// SPI read, using DMA. #[cfg(any(spi_v1, spi_v2, spi_v3))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if data.is_empty() { return Ok(()); } @@ -1013,6 +1016,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { } async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); assert_eq!(read.len(), write.len()); if read.len() == 0 { return Ok(()); @@ -1064,6 +1068,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. /// If `write` is shorter it is padded with zero bytes. pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); + self.transfer_inner(read, write).await } @@ -1071,6 +1077,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); + self.transfer_inner(data, data).await } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8047d6005..d2c361c61 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -491,7 +491,7 @@ impl<'d> UartTx<'d, Async> { /// Initiate an asynchronous UART write pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); let r = self.info.regs; @@ -510,7 +510,7 @@ impl<'d> UartTx<'d, Async> { /// Wait until transmission complete pub async fn flush(&mut self) -> Result<(), Error> { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); flush(&self.info, &self.state).await } @@ -730,7 +730,7 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous UART read pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); self.inner_read(buffer, false).await?; @@ -739,7 +739,7 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous read with idle line detection enabled pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); self.inner_read(buffer, true).await } -- cgit From 0e647105b380bad1a6e7219c708b9ccdc7067b8d Mon Sep 17 00:00:00 2001 From: Fabien Trégan Date: Sun, 31 Aug 2025 23:08:20 +0200 Subject: stm32: create new complementary pwm pin with config --- embassy-stm32/src/timer/complementary_pwm.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 19211d933..620d7858e 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -7,11 +7,12 @@ use super::simple_pwm::PwmPin; use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; use crate::Peri; use crate::dma::word::Word; -use crate::gpio::{AnyPin, OutputType}; +use crate::gpio::{AfType, AnyPin, OutputType}; pub use crate::pac::timer::vals::{Ccds, Ckd, Mms2, Ossi, Ossr}; use crate::time::Hertz; use crate::timer::TimerChannel; use crate::timer::low_level::OutputCompareMode; +use crate::timer::simple_pwm::PwmPinConfig; /// Complementary PWM pin wrapper. /// @@ -27,9 +28,27 @@ impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!( pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin)>, output_type: OutputType) -> Self { critical_section::with(|_| { pin.set_low(); - set_as_af!( - pin, - crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh) + set_as_af!(pin, AfType::output(output_type, crate::gpio::Speed::VeryHigh)); + }); + ComplementaryPwmPin { + pin: pin.into(), + phantom: PhantomData, + } + } + + /// Create a new PWM pin instance with config. + pub fn new_with_config( + pin: Peri<'d, if_afio!(impl TimerComplementaryPin)>, + pin_config: PwmPinConfig, + ) -> Self { + critical_section::with(|_| { + pin.set_low(); + #[cfg(gpio_v1)] + set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); + #[cfg(gpio_v2)] + pin.set_as_af( + pin.af_num(), + AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), ); }); ComplementaryPwmPin { -- 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(-) (limited to 'embassy-stm32') 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(-) (limited to 'embassy-stm32') 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 ac764a85a1287b9ca921352aa1fd21c78ffa0312 Mon Sep 17 00:00:00 2001 From: Roman Lim Date: Wed, 24 Sep 2025 13:52:58 +0200 Subject: allow again to set nodiv independently of master clock divider --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/sai/mod.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index ac228141e..449152485 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -91,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/spi bidirectional mode - fix: stm32/i2c v2: add stop flag on stop received - fix: stm32l47*/stm32l48* adc analog pin setup +- fix: keep stm32/sai: make NODIV independent of MCKDIV ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index ce4bc43c3..579c34c13 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -394,7 +394,8 @@ pub struct Config { pub frame_length: u16, pub clock_strobe: ClockStrobe, pub output_drive: OutputDrive, - pub master_clock_divider: Option, + pub master_clock_divider: MasterClockDivider, + pub nodiv: bool, pub is_high_impedance_on_inactive_slot: bool, pub fifo_threshold: FifoThreshold, pub companding: Companding, @@ -423,7 +424,8 @@ impl Default for Config { frame_sync_active_level_length: word::U7(16), frame_sync_definition: FrameSyncDefinition::ChannelIdentification, frame_length: 32, - master_clock_divider: None, + master_clock_divider: MasterClockDivider::DIV1, + nodiv: false, clock_strobe: ClockStrobe::Rising, output_drive: OutputDrive::Immediately, is_high_impedance_on_inactive_slot: false, @@ -677,8 +679,8 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { w.set_syncen(config.sync_input.syncen()); w.set_mono(config.stereo_mono.mono()); w.set_outdriv(config.output_drive.outdriv()); - w.set_mckdiv(config.master_clock_divider.unwrap_or(MasterClockDivider::DIV1)); - w.set_nodiv(config.master_clock_divider.is_none()); + w.set_mckdiv(config.master_clock_divider); + w.set_nodiv(config.nodiv); w.set_dmaen(true); }); -- 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(+) (limited to 'embassy-stm32') 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 +++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 171 deletions(-) (limited to 'embassy-stm32') 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()) + } +} -- 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 ++++++++++++++++++---------------------- 1 file changed, 578 insertions(+), 682 deletions(-) (limited to 'embassy-stm32') 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> { -- 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(-) (limited to 'embassy-stm32') 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 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(-) (limited to 'embassy-stm32') 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 +++++++++ 3 files changed, 980 insertions(+), 713 deletions(-) create mode 100644 embassy-stm32/src/sdmmc/sd.rs create mode 100644 embassy-stm32/src/sdmmc/sdio.rs (limited to 'embassy-stm32') 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(()) + } +} -- cgit