From 81adf1d8e4ef9382e4329ca2ace36057c9d0ea73 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 3 Nov 2025 18:51:29 -0600 Subject: stm32/adc: reorder and unify --- embassy-stm32/src/adc/ringbuffered.rs | 248 +++++----------------------------- embassy-stm32/src/adc/v2.rs | 102 +++++++++++--- embassy-stm32/src/adc/v3.rs | 32 +++++ 3 files changed, 152 insertions(+), 230 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 931ebc2b7..790eff422 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -4,6 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; #[allow(unused_imports)] use embassy_hal_internal::Peri; +use crate::adc::Adc; #[allow(unused_imports)] use crate::adc::{Instance, RxDma}; #[allow(unused_imports)] @@ -13,21 +14,12 @@ use crate::rcc; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OverrunError; -#[cfg(adc_v2)] -fn clear_interrupt_flags(r: crate::pac::adc::Adc) { - r.sr().modify(|regs| { - regs.set_eoc(false); - regs.set_ovr(false); - }); -} - pub struct RingBufferedAdc<'d, T: Instance> { - pub _phantom: PhantomData, - pub ring_buf: ReadableRingBuffer<'d, u16>, + _phantom: PhantomData, + ring_buf: ReadableRingBuffer<'d, u16>, } impl<'d, T: Instance> RingBufferedAdc<'d, T> { - #[cfg(not(adc_v2))] pub(crate) fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { //dma side setup let opts = TransferOptions { @@ -48,161 +40,24 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } } - #[cfg(adc_v2)] - fn is_on() -> bool { - T::regs().cr2().read().adon() - } - - #[cfg(adc_v2)] /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. - pub fn start(&mut self) -> Result<(), OverrunError> { - self.setup_adc(); - self.ring_buf.clear(); - - Ok(()) - } + pub fn start(&mut self) { + compiler_fence(Ordering::SeqCst); + self.ring_buf.start(); - #[cfg(adc_v2)] - fn stop(&mut self, err: OverrunError) -> Result { - self.teardown_adc(); - Err(err) + Adc::::start(); } - #[cfg(adc_v2)] - /// Stops DMA transfer. - /// It does not turn off ADC. - /// Calling `start` restarts continuous DMA transfer. - /// - /// [`start`]: #method.start - pub fn teardown_adc(&mut self) { - // Stop the DMA transfer + pub fn stop(&mut self) { self.ring_buf.request_pause(); - let r = T::regs(); - - // Stop ADC - r.cr2().modify(|reg| { - // Stop ADC - reg.set_swstart(false); - // Stop DMA - reg.set_dma(false); - }); - - r.cr1().modify(|w| { - // Disable interrupt for end of conversion - w.set_eocie(false); - // Disable interrupt for overrun - w.set_ovrie(false); - }); - - clear_interrupt_flags(r); + Adc::::stop(); compiler_fence(Ordering::SeqCst); } - #[cfg(adc_v2)] - fn setup_adc(&mut self) { - use crate::pac::adc::vals; - - compiler_fence(Ordering::SeqCst); - - self.ring_buf.start(); - - let r = T::regs(); - - // Enable ADC - let was_on = Self::is_on(); - if !was_on { - r.cr2().modify(|reg| { - reg.set_adon(false); - reg.set_swstart(false); - }); - } - - // Clear all interrupts - r.sr().modify(|regs| { - regs.set_eoc(false); - regs.set_ovr(false); - regs.set_strt(false); - }); - - r.cr1().modify(|w| { - // Enable interrupt for end of conversion - w.set_eocie(true); - // Enable interrupt for overrun - w.set_ovrie(true); - // Scanning converisons of multiple channels - w.set_scan(true); - // Continuous conversion mode - w.set_discen(false); - }); - - r.cr2().modify(|w| { - // Enable DMA mode - w.set_dma(true); - // Enable continuous conversions - w.set_cont(true); - // DMA requests are issues as long as DMA=1 and data are converted. - w.set_dds(vals::Dds::CONTINUOUS); - // EOC flag is set at the end of each conversion. - w.set_eocs(vals::Eocs::EACH_CONVERSION); - }); - - // Begin ADC conversions - T::regs().cr2().modify(|reg| { - reg.set_adon(true); - reg.set_swstart(true); - }); - - super::blocking_delay_us(3); - } - - #[cfg(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0))] - #[inline] - fn start_continuous_sampling(&mut self) { - // Start adc conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - self.ring_buf.start(); - } - - #[cfg(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0))] - #[inline] - pub fn stop_continuous_sampling(&mut self) { - // Stop adc conversion - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); - while T::regs().cr().read().adstart() {} - } - } - - #[cfg(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0))] - pub fn disable_adc(&mut self) { - self.stop_continuous_sampling(); + pub fn clear(&mut self) { self.ring_buf.clear(); - self.ring_buf.request_pause(); - } - - #[cfg(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0))] - pub fn teardown_adc(&mut self) { - self.disable_adc(); - - //disable dma control - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_dmaen(false); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(false); - }); - - //TODO: do we need to cleanup the DMA request here? - - compiler_fence(Ordering::SeqCst); } /// Reads measurements from the DMA ring buffer. @@ -257,31 +112,17 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { "Buffer size must be half the size of the ring buffer" ); - let r = T::regs(); + if !self.ring_buf.is_running() { + self.start(); + } #[cfg(adc_v2)] { - // Start background receive if it was not already started - if !r.cr2().read().dma() { - self.start()?; - } - // Clear overrun flag if set. - if r.sr().read().ovr() { - return self.stop(OverrunError); - } - - // Start background receive if it was not already started - if !r.cr().read().adstart() { - self.start_continuous_sampling(); - } - } + if T::regs().sr().read().ovr() { + self.stop(); - #[cfg(not(adc_v2))] - { - // Start background receive if it was not already started - if !r.cr().read().adstart() { - self.start_continuous_sampling(); + return Err(OverrunError); } } @@ -297,50 +138,29 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// Receive in the background is terminated if an error is returned. /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`. pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result { - let r = T::regs(); + if !self.ring_buf.is_running() { + self.start(); + } #[cfg(adc_v2)] { - // Start background receive if it was not already started - if !r.cr2().read().dma() { - self.start()?; - } - // Clear overrun flag if set. - if r.sr().read().ovr() { - return self.stop(OverrunError); - } + if T::regs().sr().read().ovr() { + self.stop(); - loop { - match self.ring_buf.read(buf) { - Ok((0, _)) => {} - Ok((len, _)) => { - return Ok(len); - } - Err(_) => { - return self.stop(OverrunError); - } - } + return Err(OverrunError); } } + loop { + match self.ring_buf.read(buf) { + Ok((0, _)) => {} + Ok((len, _)) => { + return Ok(len); + } + Err(_) => { + self.stop(); - #[cfg(not(adc_v2))] - { - // Start background receive if it was not already started - if !r.cr().read().adstart() { - self.start_continuous_sampling(); - } - - loop { - match self.ring_buf.read(buf) { - Ok((0, _)) => {} - Ok((len, _)) => { - return Ok(len); - } - Err(_) => { - self.stop_continuous_sampling(); - return Err(OverrunError); - } + return Err(OverrunError); } } } @@ -349,7 +169,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { impl Drop for RingBufferedAdc<'_, T> { fn drop(&mut self) { - self.teardown_adc(); + Adc::::teardown_adc(); + + compiler_fence(Ordering::SeqCst); + + self.ring_buf.request_pause(); rcc::disable::(); } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e57cd19b3..90c6294d2 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,9 +1,9 @@ -use core::marker::PhantomData; use core::mem; +use core::sync::atomic::{Ordering, compiler_fence}; use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; -use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; +use crate::pac::adc::vals; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::{Peri, rcc}; @@ -11,6 +11,13 @@ use crate::{Peri, rcc}; mod ringbuffered; pub use ringbuffered::RingBufferedAdc; +fn clear_interrupt_flags(r: crate::pac::adc::Adc) { + r.sr().modify(|regs| { + regs.set_eoc(false); + regs.set_ovr(false); + }); +} + /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. @@ -134,18 +141,6 @@ where ) -> RingBufferedAdc<'d, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - let opts: crate::dma::TransferOptions = TransferOptions { - half_transfer_ir: true, - priority: Priority::VeryHigh, - ..Default::default() - }; - - // Safety: we forget the struct before this function returns. - let rx_src = T::regs().dr().as_ptr() as *mut u16; - let request = dma.request(); - - let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) }; - T::regs().cr2().modify(|reg| { reg.set_adon(true); }); @@ -165,13 +160,59 @@ where Self::set_channel_sample_time(channel.channel(), sample_time); } + compiler_fence(Ordering::SeqCst); + + let r = T::regs(); + + // Clear all interrupts + r.sr().modify(|regs| { + regs.set_eoc(false); + regs.set_ovr(false); + regs.set_strt(false); + }); + + r.cr1().modify(|w| { + // Enable interrupt for end of conversion + w.set_eocie(true); + // Enable interrupt for overrun + w.set_ovrie(true); + // Scanning converisons of multiple channels + w.set_scan(true); + // Continuous conversion mode + w.set_discen(false); + }); + + r.cr2().modify(|w| { + // Enable DMA mode + w.set_dma(true); + // Enable continuous conversions + w.set_cont(true); + // DMA requests are issues as long as DMA=1 and data are converted. + w.set_dds(vals::Dds::CONTINUOUS); + // EOC flag is set at the end of each conversion. + w.set_eocs(vals::Eocs::EACH_CONVERSION); + }); + // Don't disable the clock mem::forget(self); - RingBufferedAdc { - _phantom: PhantomData, - ring_buf, - } + RingBufferedAdc::new(dma, dma_buf) + } + + pub(super) fn start() { + // Begin ADC conversions + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + reg.set_swstart(true); + }); + } + + pub(super) fn stop() { + // Stop ADC + T::regs().cr2().modify(|reg| { + // Stop ADC + reg.set_swstart(false); + }); } pub fn set_sample_time(&mut self, sample_time: SampleTime) { @@ -260,6 +301,31 @@ where T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } + + pub(super) fn teardown_adc() { + let r = T::regs(); + + // Stop ADC + r.cr2().modify(|reg| { + // Stop ADC + reg.set_swstart(false); + // Stop ADC + reg.set_adon(false); + // Stop DMA + reg.set_dma(false); + }); + + r.cr1().modify(|w| { + // Disable interrupt for end of conversion + w.set_eocie(false); + // Disable interrupt for overrun + w.set_ovrie(false); + }); + + clear_interrupt_flags(r); + + compiler_fence(Ordering::SeqCst); + } } impl<'d, T: Instance> Drop for Adc<'d, T> { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8a0cc0fcf..6da0adb44 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -181,6 +181,38 @@ impl<'d, T: Instance> Adc<'d, T> { blocking_delay_us(1); } + #[cfg(any(adc_v3, adc_g0, adc_u0))] + pub(super) fn start() { + // Start adc conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + } + + #[cfg(any(adc_v3, adc_g0, adc_u0))] + pub(super) fn stop() { + // Stop adc conversion + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(true); + }); + while T::regs().cr().read().adstart() {} + } + } + + #[cfg(any(adc_v3, adc_g0, adc_u0))] + pub(super) fn teardown_adc() { + //disable dma control + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| { + reg.set_dmaen(false); + }); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_dmaen(false); + }); + } + /// Initialize the ADC leaving any analog clock at reset value. /// For G0 and WL, this is the async clock without prescaler. pub fn new(adc: Peri<'d, T>) -> Self { -- cgit