From 14a047a9ad75709e0bde8b0fa71f3b4bddedc576 Mon Sep 17 00:00:00 2001 From: maor malka Date: Sun, 24 Aug 2025 21:36:33 -0400 Subject: [PATCH 01/14] stm32/adc/v3: added support for DMA based adc sampling --- embassy-stm32/src/adc/v3.rs | 274 +++++++++++++++++++++++++++- examples/stm32l4/src/bin/adc_dma.rs | 49 +++++ 2 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 examples/stm32l4/src/bin/adc_dma.rs diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index a2e42fe523..6d874dbba9 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,10 +3,13 @@ use pac::adc::vals::Dmacfg; #[cfg(adc_v3)] use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; + use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; -use crate::dma::Transfer; +use crate::dma::{ReadableRingBuffer, Transfer, TransferOptions}; use crate::{pac, rcc, Peri}; /// Default VREF voltage used for sample conversion to millivolts. @@ -107,6 +110,15 @@ pub enum Averaging { Samples128, Samples256, } + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OverrunError; + +pub struct RingBufferedAdc<'d, T: Instance> { + _phantom: PhantomData, + ring_buf: ReadableRingBuffer<'d, u16>, +} + impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); @@ -449,6 +461,122 @@ impl<'d, T: Instance> Adc<'d, T> { }); } + /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. + /// + /// The `dma_buf` should be large enough to prevent DMA buffer overrun. + /// The length of the `dma_buf` should be a multiple of the ADC channel count. + /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. + /// + /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. + /// It is critical to call `read` frequently to prevent DMA buffer overrun. + /// + /// [`read`]: #method.read + pub fn into_ring_buffered<'a>( + &mut self, + dma: Peri<'a, impl RxDma>, + dma_buf: &'a mut [u16], + sequence: impl ExactSizeIterator, SampleTime)>, + ) -> RingBufferedAdc<'a, T> { + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() <= 16, + "Asynchronous read sequence cannot be more than 16 in length" + ); + // reset conversions and enable the adc + Self::cancel_conversions(); + self.enable(); + + //adc side setup + + // Set sequence length + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().sqr1().modify(|w| { + w.set_l(sequence.len() as u8 - 1); + }); + + #[cfg(any(adc_g0, adc_u0))] + let mut channel_mask = 0; + + // Configure channels and ranks + for (_i, (channel, sample_time)) in sequence.enumerate() { + Self::configure_channel(channel, sample_time); + + // Each channel is sampled according to sequence + #[cfg(not(any(adc_g0, adc_u0)))] + match _i { + 0..=3 => { + T::regs().sqr1().modify(|w| { + w.set_sq(_i, channel.channel()); + }); + } + 4..=8 => { + T::regs().sqr2().modify(|w| { + w.set_sq(_i - 4, channel.channel()); + }); + } + 9..=13 => { + T::regs().sqr3().modify(|w| { + w.set_sq(_i - 9, channel.channel()); + }); + } + 14..=15 => { + T::regs().sqr4().modify(|w| { + w.set_sq(_i - 14, channel.channel()); + }); + } + _ => unreachable!(), + } + } + + //dma side setup + let opts = TransferOptions { + half_transfer_ir: true, + circular: true, + ..Default::default() + }; + + // 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) }; + + // 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(any(adc_g0, adc_u0))] + T::regs().chselr().modify(|reg| { + reg.set_chsel(channel_mask); + }); + + // Set continuous mode with Circular dma. + // Clear overrun flag before starting transfer. + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + }); + + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::CIRCULAR); + reg.set_dmaen(true); + }); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::CIRCULAR); + reg.set_dmaen(true); + }); + + RingBufferedAdc { + _phantom: PhantomData, + ring_buf, + } + } + fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." @@ -553,3 +681,147 @@ impl<'d, T: Instance> Adc<'d, T> { } } } + +impl<'d, T: Instance> RingBufferedAdc<'d, T> { + #[inline] + fn start_continous_sampling(&mut self) { + // Start adc conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + self.ring_buf.start(); + } + + #[inline] + pub fn stop_continous_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() {} + } + } + pub fn disable_adc(&mut self) { + self.stop_continous_sampling(); + self.ring_buf.clear(); + self.ring_buf.request_pause(); + } + + 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. + /// + /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. + /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. + /// + /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `sequence`. + /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. + /// For example if 2 channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. + /// + /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly running tasks + /// Otherwise, you'll see constant Overrun errors occuring, this means that you're sampling too quickly for the task to handle, and you may need to increase the buffer size. + /// Example: + /// ```rust,ignore + /// const DMA_BUF_LEN: usize = 120; + /// use embassy_stm32::adc::{Adc, AdcChannel} + /// + /// let mut adc = Adc::new(p.ADC1); + /// let mut adc_pin0 = p.PA0.degrade_adc(); + /// let mut adc_pin1 = p.PA1.degrade_adc(); + /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; + /// + /// let mut ring_buffered_adc: RingBufferedAdc = adc.into_ring_buffered( + /// p.DMA2_CH0, + /// adc_dma_buf, [ + /// (&mut *adc_pin0, SampleTime::CYCLES160_5), + /// (&mut *adc_pin1, SampleTime::CYCLES160_5), + /// ].into_iter()); + /// + /// + /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; + /// loop { + /// match ring_buffered_adc.read(&mut measurements).await { + /// Ok(_) => { + /// defmt::info!("adc1: {}", measurements); + /// } + /// Err(e) => { + /// defmt::warn!("Error: {:?}", e); + /// } + /// } + /// } + /// ``` + /// + /// + /// [`teardown_adc`]: #method.teardown_adc + /// [`start_continous_sampling`]: #method.start_continous_sampling + pub async fn read(&mut self, measurements: &mut [u16]) -> Result { + assert_eq!( + self.ring_buf.capacity() / 2, + measurements.len(), + "Buffer size must be half the size of the ring buffer" + ); + + let r = T::regs(); + + // Start background receive if it was not already started + if !r.cr().read().adstart() { + self.start_continous_sampling(); + } + + self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) + } + + /// Read bytes that are readily available in the ring buffer. + /// If no bytes are currently available in the buffer the call waits until the some + /// bytes are available (at least one byte and at most half the buffer size) + /// + /// Background receive is started if `start_continous_sampling()` has not been previously called. + /// + /// Receive in the background is terminated if an error is returned. + /// It must then manually be started again by calling `start_continous_sampling()` or by re-calling `blocking_read()`. + pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result { + let r = T::regs(); + + // Start background receive if it was not already started + if !r.cr().read().adstart() { + self.start_continous_sampling(); + } + + loop { + match self.ring_buf.read(buf) { + Ok((0, _)) => {} + Ok((len, _)) => { + return Ok(len); + } + Err(_) => { + self.stop_continous_sampling(); + return Err(OverrunError); + } + } + } + } +} + +impl Drop for RingBufferedAdc<'_, T> { + fn drop(&mut self) { + self.teardown_adc(); + rcc::disable::(); + } +} diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs new file mode 100644 index 0000000000..1769c735a2 --- /dev/null +++ b/examples/stm32l4/src/bin/adc_dma.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; +use embassy_stm32::Config; +use {defmt_rtt as _, panic_probe as _}; + +const DMA_BUF_LEN: usize = 512; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let config = Config::default(); + + let p = embassy_stm32::init(config); + + let mut adc = Adc::new(p.ADC1); + let mut adc_pin0 = p.PA0.degrade_adc(); + let mut adc_pin1 = p.PA1.degrade_adc(); + let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; + let mut measurements = [0u16; DMA_BUF_LEN / 2]; + let mut ring_buffered_adc = adc.into_ring_buffered( + p.DMA1_CH1, + &mut adc_dma_buf, + [ + (&mut adc_pin0, SampleTime::CYCLES640_5), + (&mut adc_pin1, SampleTime::CYCLES640_5), + ] + .into_iter(), + ); + + info!("starting measurement loop"); + loop { + match ring_buffered_adc.read(&mut measurements).await { + Ok(_) => { + //note: originally there was a print here showing all the samples, + //but even that takes too much time and would cause adc overruns + info!("adc1 first 10 samples: {}",measurements[0..10]); + } + Err(e) => { + warn!("Error: {:?}", e); + } + } + } + +} From 0b8da5ab8f6469bdf2adf7462e7ebedee93dde3f Mon Sep 17 00:00:00 2001 From: maor malka Date: Sun, 24 Aug 2025 21:42:30 -0400 Subject: [PATCH 02/14] stm32l4/example/adc_dma: missing clock configuration --- examples/stm32l4/src/bin/adc_dma.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index 1769c735a2..a5b7b0c5e9 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs @@ -13,8 +13,11 @@ const DMA_BUF_LEN: usize = 512; async fn main(_spawner: Spawner) { info!("Hello World!"); - let config = Config::default(); - + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.mux.adcsel = mux::Adcsel::SYS; + } let p = embassy_stm32::init(config); let mut adc = Adc::new(p.ADC1); @@ -36,14 +39,13 @@ async fn main(_spawner: Spawner) { loop { match ring_buffered_adc.read(&mut measurements).await { Ok(_) => { - //note: originally there was a print here showing all the samples, + //note: originally there was a print here showing all the samples, //but even that takes too much time and would cause adc overruns - info!("adc1 first 10 samples: {}",measurements[0..10]); + info!("adc1 first 10 samples: {}", measurements[0..10]); } Err(e) => { warn!("Error: {:?}", e); } } } - } From 135070040688a4e5b50b84718526b630714d1a13 Mon Sep 17 00:00:00 2001 From: maor malka Date: Sun, 24 Aug 2025 21:53:19 -0400 Subject: [PATCH 03/14] stm32/adc/v3: build fix attempt --- embassy-stm32/src/adc/v3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 6d874dbba9..bc5b80ccda 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -9,6 +9,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; +#[cfg(adc_v3)] use crate::dma::{ReadableRingBuffer, Transfer, TransferOptions}; use crate::{pac, rcc, Peri}; From e5d4ef42699859fbcbcee083842195c8d4f7b8b0 Mon Sep 17 00:00:00 2001 From: maor malka Date: Sun, 24 Aug 2025 22:00:08 -0400 Subject: [PATCH 04/14] stm32/adc/v3: build formatting fixes --- embassy-stm32/src/adc/v3.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index bc5b80ccda..03864b171a 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,11 +1,11 @@ +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; + use cfg_if::cfg_if; use pac::adc::vals::Dmacfg; #[cfg(adc_v3)] use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; -use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; - use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; From 756ec7f1837a714f9c8304e2b2b21b542e0260d6 Mon Sep 17 00:00:00 2001 From: maor malka Date: Sun, 24 Aug 2025 22:04:15 -0400 Subject: [PATCH 05/14] stm32/adc/v3: updated changelog and cfg fence for DMA methods --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/adc/v3.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 9cd4d5951d..90d38a4250 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: Fix performing a hash after performing a hmac - chore: Updated stm32-metapac and stm32-data dependencies - fix: Fix XSPI not disabling alternate bytes when they were previously enabled +- feat: stm32/adc/v3: added support for Continous DMA configuration ## 0.3.0 - 2025-08-12 diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 03864b171a..8062fa1695 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -471,7 +471,8 @@ impl<'d, T: Instance> Adc<'d, T> { /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. /// It is critical to call `read` frequently to prevent DMA buffer overrun. /// - /// [`read`]: #method.read + /// [`read`]: #method.read + #[cfg(adc_v3)] pub fn into_ring_buffered<'a>( &mut self, dma: Peri<'a, impl RxDma>, @@ -683,6 +684,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } +#[cfg(adc_v3)] impl<'d, T: Instance> RingBufferedAdc<'d, T> { #[inline] fn start_continous_sampling(&mut self) { From 75484f4f51847a92e2df1e8319debec61cd7aca2 Mon Sep 17 00:00:00 2001 From: maor malka Date: Sun, 24 Aug 2025 22:05:25 -0400 Subject: [PATCH 06/14] stm32/adc/v3: build formatting fixes --- embassy-stm32/src/adc/v3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8062fa1695..a52141a346 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -471,7 +471,7 @@ impl<'d, T: Instance> Adc<'d, T> { /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. /// It is critical to call `read` frequently to prevent DMA buffer overrun. /// - /// [`read`]: #method.read + /// [`read`]: #method.read #[cfg(adc_v3)] pub fn into_ring_buffered<'a>( &mut self, From 6b8d375813116fba0e04aa28e23ded8ab077729a Mon Sep 17 00:00:00 2001 From: maor malka Date: Tue, 26 Aug 2025 22:15:34 -0400 Subject: [PATCH 07/14] stm32/adc/v3: moved ringbuffered to seperate file --- embassy-stm32/src/adc/ringbuffered_v3.rs | 181 ++++++++++++++++++++++ embassy-stm32/src/adc/v3.rs | 182 +---------------------- 2 files changed, 189 insertions(+), 174 deletions(-) create mode 100644 embassy-stm32/src/adc/ringbuffered_v3.rs diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs new file mode 100644 index 0000000000..655ae712f5 --- /dev/null +++ b/embassy-stm32/src/adc/ringbuffered_v3.rs @@ -0,0 +1,181 @@ +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use embassy_hal_internal::Peri; + +use crate::dma::{ReadableRingBuffer, TransferOptions}; + +use crate::adc::Instance; +use crate::adc::RxDma; +use crate::rcc; + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OverrunError; + +pub struct RingBufferedAdc<'d, T: Instance> { + pub _phantom: PhantomData, + pub ring_buf: ReadableRingBuffer<'d, u16>, +} + +impl<'d, T: Instance> RingBufferedAdc<'d, T> { + pub fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { + //dma side setup + let opts = TransferOptions { + half_transfer_ir: true, + circular: true, + ..Default::default() + }; + + // 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) }; + + Self { + _phantom: PhantomData, + ring_buf, + } + } + + #[inline] + fn start_continous_sampling(&mut self) { + // Start adc conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + self.ring_buf.start(); + } + + #[inline] + pub fn stop_continous_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() {} + } + } + pub fn disable_adc(&mut self) { + self.stop_continous_sampling(); + self.ring_buf.clear(); + self.ring_buf.request_pause(); + } + + 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. + /// + /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. + /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. + /// + /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `sequence`. + /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. + /// For example if 2 channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. + /// + /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly running tasks + /// Otherwise, you'll see constant Overrun errors occuring, this means that you're sampling too quickly for the task to handle, and you may need to increase the buffer size. + /// Example: + /// ```rust,ignore + /// const DMA_BUF_LEN: usize = 120; + /// use embassy_stm32::adc::{Adc, AdcChannel} + /// + /// let mut adc = Adc::new(p.ADC1); + /// let mut adc_pin0 = p.PA0.degrade_adc(); + /// let mut adc_pin1 = p.PA1.degrade_adc(); + /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; + /// + /// let mut ring_buffered_adc: RingBufferedAdc = adc.into_ring_buffered( + /// p.DMA2_CH0, + /// adc_dma_buf, [ + /// (&mut *adc_pin0, SampleTime::CYCLES160_5), + /// (&mut *adc_pin1, SampleTime::CYCLES160_5), + /// ].into_iter()); + /// + /// + /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; + /// loop { + /// match ring_buffered_adc.read(&mut measurements).await { + /// Ok(_) => { + /// defmt::info!("adc1: {}", measurements); + /// } + /// Err(e) => { + /// defmt::warn!("Error: {:?}", e); + /// } + /// } + /// } + /// ``` + /// + /// + /// [`teardown_adc`]: #method.teardown_adc + /// [`start_continous_sampling`]: #method.start_continous_sampling + pub async fn read(&mut self, measurements: &mut [u16]) -> Result { + assert_eq!( + self.ring_buf.capacity() / 2, + measurements.len(), + "Buffer size must be half the size of the ring buffer" + ); + + let r = T::regs(); + + // Start background receive if it was not already started + if !r.cr().read().adstart() { + self.start_continous_sampling(); + } + + self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) + } + + /// Read bytes that are readily available in the ring buffer. + /// If no bytes are currently available in the buffer the call waits until the some + /// bytes are available (at least one byte and at most half the buffer size) + /// + /// Background receive is started if `start_continous_sampling()` has not been previously called. + /// + /// Receive in the background is terminated if an error is returned. + /// It must then manually be started again by calling `start_continous_sampling()` or by re-calling `blocking_read()`. + pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result { + let r = T::regs(); + + // Start background receive if it was not already started + if !r.cr().read().adstart() { + self.start_continous_sampling(); + } + + loop { + match self.ring_buf.read(buf) { + Ok((0, _)) => {} + Ok((len, _)) => { + return Ok(len); + } + Err(_) => { + self.stop_continous_sampling(); + return Err(OverrunError); + } + } + } + } +} + +impl Drop for RingBufferedAdc<'_, T> { + fn drop(&mut self) { + self.teardown_adc(); + rcc::disable::(); + } +} diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index a52141a346..30b04fc812 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,6 +1,3 @@ -use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; - use cfg_if::cfg_if; use pac::adc::vals::Dmacfg; #[cfg(adc_v3)] @@ -9,8 +6,14 @@ use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; + #[cfg(adc_v3)] -use crate::dma::{ReadableRingBuffer, Transfer, TransferOptions}; +mod ringbuffered_v3; + +#[cfg(adc_v3)] +use ringbuffered_v3::RingBufferedAdc; + +use crate::dma::Transfer; use crate::{pac, rcc, Peri}; /// Default VREF voltage used for sample conversion to millivolts. @@ -112,14 +115,6 @@ pub enum Averaging { Samples256, } -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OverrunError; - -pub struct RingBufferedAdc<'d, T: Instance> { - _phantom: PhantomData, - ring_buf: ReadableRingBuffer<'d, u16>, -} - impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); @@ -531,19 +526,6 @@ impl<'d, T: Instance> Adc<'d, T> { } } - //dma side setup - let opts = TransferOptions { - half_transfer_ir: true, - circular: true, - ..Default::default() - }; - - // 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) }; - // 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. @@ -573,10 +555,7 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_dmaen(true); }); - RingBufferedAdc { - _phantom: PhantomData, - ring_buf, - } + RingBufferedAdc::new(dma, dma_buf) } fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { @@ -683,148 +662,3 @@ impl<'d, T: Instance> Adc<'d, T> { } } } - -#[cfg(adc_v3)] -impl<'d, T: Instance> RingBufferedAdc<'d, T> { - #[inline] - fn start_continous_sampling(&mut self) { - // Start adc conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - self.ring_buf.start(); - } - - #[inline] - pub fn stop_continous_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() {} - } - } - pub fn disable_adc(&mut self) { - self.stop_continous_sampling(); - self.ring_buf.clear(); - self.ring_buf.request_pause(); - } - - 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. - /// - /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. - /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. - /// - /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `sequence`. - /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. - /// For example if 2 channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. - /// - /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly running tasks - /// Otherwise, you'll see constant Overrun errors occuring, this means that you're sampling too quickly for the task to handle, and you may need to increase the buffer size. - /// Example: - /// ```rust,ignore - /// const DMA_BUF_LEN: usize = 120; - /// use embassy_stm32::adc::{Adc, AdcChannel} - /// - /// let mut adc = Adc::new(p.ADC1); - /// let mut adc_pin0 = p.PA0.degrade_adc(); - /// let mut adc_pin1 = p.PA1.degrade_adc(); - /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; - /// - /// let mut ring_buffered_adc: RingBufferedAdc = adc.into_ring_buffered( - /// p.DMA2_CH0, - /// adc_dma_buf, [ - /// (&mut *adc_pin0, SampleTime::CYCLES160_5), - /// (&mut *adc_pin1, SampleTime::CYCLES160_5), - /// ].into_iter()); - /// - /// - /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; - /// loop { - /// match ring_buffered_adc.read(&mut measurements).await { - /// Ok(_) => { - /// defmt::info!("adc1: {}", measurements); - /// } - /// Err(e) => { - /// defmt::warn!("Error: {:?}", e); - /// } - /// } - /// } - /// ``` - /// - /// - /// [`teardown_adc`]: #method.teardown_adc - /// [`start_continous_sampling`]: #method.start_continous_sampling - pub async fn read(&mut self, measurements: &mut [u16]) -> Result { - assert_eq!( - self.ring_buf.capacity() / 2, - measurements.len(), - "Buffer size must be half the size of the ring buffer" - ); - - let r = T::regs(); - - // Start background receive if it was not already started - if !r.cr().read().adstart() { - self.start_continous_sampling(); - } - - self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) - } - - /// Read bytes that are readily available in the ring buffer. - /// If no bytes are currently available in the buffer the call waits until the some - /// bytes are available (at least one byte and at most half the buffer size) - /// - /// Background receive is started if `start_continous_sampling()` has not been previously called. - /// - /// Receive in the background is terminated if an error is returned. - /// It must then manually be started again by calling `start_continous_sampling()` or by re-calling `blocking_read()`. - pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result { - let r = T::regs(); - - // Start background receive if it was not already started - if !r.cr().read().adstart() { - self.start_continous_sampling(); - } - - loop { - match self.ring_buf.read(buf) { - Ok((0, _)) => {} - Ok((len, _)) => { - return Ok(len); - } - Err(_) => { - self.stop_continous_sampling(); - return Err(OverrunError); - } - } - } - } -} - -impl Drop for RingBufferedAdc<'_, T> { - fn drop(&mut self) { - self.teardown_adc(); - rcc::disable::(); - } -} From e48dc90edc3277d1b406900d16b35653aafdbe3a Mon Sep 17 00:00:00 2001 From: maor malka Date: Thu, 28 Aug 2025 18:15:43 -0400 Subject: [PATCH 08/14] stm32/adc/v3: rustfmt shananigans --- embassy-stm32/src/adc/ringbuffered_v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs index 655ae712f5..7ff37f68d7 100644 --- a/embassy-stm32/src/adc/ringbuffered_v3.rs +++ b/embassy-stm32/src/adc/ringbuffered_v3.rs @@ -1,11 +1,11 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; + use embassy_hal_internal::Peri; +use crate::adc::{Instance, RxDma}; use crate::dma::{ReadableRingBuffer, TransferOptions}; -use crate::adc::Instance; -use crate::adc::RxDma; use crate::rcc; #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 1db247f5a493f178751ba083483009bfa31c7899 Mon Sep 17 00:00:00 2001 From: maor malka Date: Thu, 28 Aug 2025 18:16:56 -0400 Subject: [PATCH 09/14] rustfmt --- embassy-stm32/src/adc/ringbuffered_v3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs index 7ff37f68d7..d7af2322d9 100644 --- a/embassy-stm32/src/adc/ringbuffered_v3.rs +++ b/embassy-stm32/src/adc/ringbuffered_v3.rs @@ -5,7 +5,6 @@ use embassy_hal_internal::Peri; use crate::adc::{Instance, RxDma}; use crate::dma::{ReadableRingBuffer, TransferOptions}; - use crate::rcc; #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 5046a8a4a117f68c2792fac8dbbdb50fb0b1e3d8 Mon Sep 17 00:00:00 2001 From: maor malka Date: Mon, 6 Oct 2025 07:50:48 -0400 Subject: [PATCH 10/14] stm32/adc/v3: * spelling mistakes fixed * added required changes to ringbufferedadc to support G0 --- embassy-stm32/CHANGELOG.md | 2 +- embassy-stm32/src/adc/ringbuffered_v3.rs | 18 +++++++++--------- embassy-stm32/src/adc/v3.rs | 11 ++++++++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b661e0bae6..6e069b22d4 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -43,7 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - chore: Updated stm32-metapac and stm32-data dependencies - feat: stm32/adc/v3: allow DMA reads to loop through enable channels - fix: Fix XSPI not disabling alternate bytes when they were previously enabled -- feat: stm32/adc/v3: added support for Continous DMA configuration +- feat: stm32/adc/v3: added support for Continuous DMA configuration - fix: Fix stm32h7rs init when using external flash via XSPI - feat: Add Adc::new_with_clock() to configure analog clock - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs index d7af2322d9..a2c9f2bca1 100644 --- a/embassy-stm32/src/adc/ringbuffered_v3.rs +++ b/embassy-stm32/src/adc/ringbuffered_v3.rs @@ -37,7 +37,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } #[inline] - fn start_continous_sampling(&mut self) { + fn start_continuous_sampling(&mut self) { // Start adc conversion T::regs().cr().modify(|reg| { reg.set_adstart(true); @@ -46,7 +46,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } #[inline] - pub fn stop_continous_sampling(&mut self) { + 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| { @@ -56,7 +56,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } } pub fn disable_adc(&mut self) { - self.stop_continous_sampling(); + self.stop_continuous_sampling(); self.ring_buf.clear(); self.ring_buf.request_pause(); } @@ -123,7 +123,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// /// /// [`teardown_adc`]: #method.teardown_adc - /// [`start_continous_sampling`]: #method.start_continous_sampling + /// [`start_continuous_sampling`]: #method.start_continuous_sampling pub async fn read(&mut self, measurements: &mut [u16]) -> Result { assert_eq!( self.ring_buf.capacity() / 2, @@ -135,7 +135,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { // Start background receive if it was not already started if !r.cr().read().adstart() { - self.start_continous_sampling(); + self.start_continuous_sampling(); } self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) @@ -145,16 +145,16 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// If no bytes are currently available in the buffer the call waits until the some /// bytes are available (at least one byte and at most half the buffer size) /// - /// Background receive is started if `start_continous_sampling()` has not been previously called. + /// Background receive is started if `start_continuous_sampling()` has not been previously called. /// /// Receive in the background is terminated if an error is returned. - /// It must then manually be started again by calling `start_continous_sampling()` or by re-calling `blocking_read()`. + /// 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(); // Start background receive if it was not already started if !r.cr().read().adstart() { - self.start_continous_sampling(); + self.start_continuous_sampling(); } loop { @@ -164,7 +164,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { return Ok(len); } Err(_) => { - self.stop_continous_sampling(); + self.stop_continuous_sampling(); return Err(OverrunError); } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index f714e030fd..ef68fe2233 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -13,10 +13,10 @@ use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; -#[cfg(adc_v3)] +#[cfg(adc_v3, adc_g0)] mod ringbuffered_v3; -#[cfg(adc_v3)] +#[cfg(adc_v3, adc_g0)] use ringbuffered_v3::RingBufferedAdc; use crate::dma::Transfer; @@ -576,7 +576,7 @@ impl<'d, T: Instance> Adc<'d, T> { /// It is critical to call `read` frequently to prevent DMA buffer overrun. /// /// [`read`]: #method.read - #[cfg(adc_v3)] + #[cfg(adc_v3, adc_g0)] pub fn into_ring_buffered<'a>( &mut self, dma: Peri<'a, impl RxDma>, @@ -633,6 +633,11 @@ impl<'d, T: Instance> Adc<'d, T> { } _ => unreachable!(), } + + #[cfg(any(adc_g0, adc_u0))] + { + channel_mask |= 1 << channel.channel(); + } } // On G0 and U0 enabled channels are sampled from 0 to last channel. From 0a97a1d6536be20e8aea1da2d7a6dcd16ca679d4 Mon Sep 17 00:00:00 2001 From: maor malka Date: Mon, 6 Oct 2025 07:56:20 -0400 Subject: [PATCH 11/14] stm32/adc/v3: syntax errors :( --- embassy-stm32/src/adc/v3.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index ef68fe2233..39f9ee4631 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -13,10 +13,10 @@ use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; -#[cfg(adc_v3, adc_g0)] +#[cfg(any(adc_v3, adc_g0))] mod ringbuffered_v3; -#[cfg(adc_v3, adc_g0)] +#[cfg(any(adc_v3, adc_g0))] use ringbuffered_v3::RingBufferedAdc; use crate::dma::Transfer; @@ -576,7 +576,7 @@ impl<'d, T: Instance> Adc<'d, T> { /// It is critical to call `read` frequently to prevent DMA buffer overrun. /// /// [`read`]: #method.read - #[cfg(adc_v3, adc_g0)] + #[cfg(any(adc_v3, adc_g0))] pub fn into_ring_buffered<'a>( &mut self, dma: Peri<'a, impl RxDma>, From cd91fe3b30dbb3d5b3c9c7d9e7cb151d721fb8d5 Mon Sep 17 00:00:00 2001 From: maor malka Date: Tue, 7 Oct 2025 07:57:29 -0400 Subject: [PATCH 12/14] stm32/adc/v3: merged newer adc_g0 configuration method to intoRingBuffered --- embassy-stm32/src/adc/v3.rs | 109 ++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 39f9ee4631..0b9b80db2e 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -13,10 +13,10 @@ use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; -#[cfg(any(adc_v3, adc_g0))] +#[cfg(any(adc_v3, adc_g0, adc_u0))] mod ringbuffered_v3; -#[cfg(any(adc_v3, adc_g0))] +#[cfg(any(adc_v3, adc_g0, adc_u0))] use ringbuffered_v3::RingBufferedAdc; use crate::dma::Transfer; @@ -601,53 +601,78 @@ impl<'d, T: Instance> Adc<'d, T> { w.set_l(sequence.len() as u8 - 1); }); - #[cfg(any(adc_g0, adc_u0))] - let mut channel_mask = 0; + #[cfg(adc_g0)] + { + let mut sample_times = Vec::::new(); + + T::regs().chselr().write(|chselr| { + T::regs().smpr().write(|smpr| { + for (channel, sample_time) in sequence { + chselr.set_chsel(channel.channel.into(), true); + if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { + smpr.set_smpsel(channel.channel.into(), (i as u8).into()); + } else { + smpr.set_sample_time(sample_times.len(), sample_time); + if let Err(_) = sample_times.push(sample_time) { + panic!( + "Implementation is limited to {} unique sample times among all channels.", + SAMPLE_TIMES_CAPACITY + ); + } + } + } + }) + }); + } + #[cfg(not(adc_g0))] + { + #[cfg(adc_u0)] + let mut channel_mask = 0; - // Configure channels and ranks - for (_i, (channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(channel, sample_time); + // Configure channels and ranks + for (_i, (channel, sample_time)) in sequence.enumerate() { + Self::configure_channel(channel, sample_time); - // Each channel is sampled according to sequence - #[cfg(not(any(adc_g0, adc_u0)))] - match _i { - 0..=3 => { - T::regs().sqr1().modify(|w| { - w.set_sq(_i, channel.channel()); - }); - } - 4..=8 => { - T::regs().sqr2().modify(|w| { - w.set_sq(_i - 4, channel.channel()); - }); - } - 9..=13 => { - T::regs().sqr3().modify(|w| { - w.set_sq(_i - 9, channel.channel()); - }); + // Each channel is sampled according to sequence + #[cfg(not(any(adc_g0, adc_u0)))] + match _i { + 0..=3 => { + T::regs().sqr1().modify(|w| { + w.set_sq(_i, channel.channel()); + }); + } + 4..=8 => { + T::regs().sqr2().modify(|w| { + w.set_sq(_i - 4, channel.channel()); + }); + } + 9..=13 => { + T::regs().sqr3().modify(|w| { + w.set_sq(_i - 9, channel.channel()); + }); + } + 14..=15 => { + T::regs().sqr4().modify(|w| { + w.set_sq(_i - 14, channel.channel()); + }); + } + _ => unreachable!(), } - 14..=15 => { - T::regs().sqr4().modify(|w| { - w.set_sq(_i - 14, channel.channel()); - }); + + #[cfg(adc_u0)] + { + channel_mask |= 1 << channel.channel(); } - _ => unreachable!(), } - #[cfg(any(adc_g0, adc_u0))] - { - channel_mask |= 1 << channel.channel(); - } + // 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| { + reg.set_chsel(channel_mask); + }); } - - // 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(any(adc_g0, adc_u0))] - T::regs().chselr().modify(|reg| { - reg.set_chsel(channel_mask); - }); - // Set continuous mode with Circular dma. // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { From cb0175a89f072d38393368ef380d9db8e3994740 Mon Sep 17 00:00:00 2001 From: maor malka Date: Tue, 7 Oct 2025 15:03:31 -0400 Subject: [PATCH 13/14] stm32/adc/v3: missing cfg option for adc_u0 fro into_ring_buffered --- embassy-stm32/src/adc/ringbuffered_v3.rs | 2 +- embassy-stm32/src/adc/v3.rs | 2 +- examples/stm32l4/src/bin/adc_dma.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs index a2c9f2bca1..b58630585f 100644 --- a/embassy-stm32/src/adc/ringbuffered_v3.rs +++ b/embassy-stm32/src/adc/ringbuffered_v3.rs @@ -1,5 +1,5 @@ use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use embassy_hal_internal::Peri; diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8dcb5d04ee..d9a3ce21da 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -576,7 +576,7 @@ impl<'d, T: Instance> Adc<'d, T> { /// It is critical to call `read` frequently to prevent DMA buffer overrun. /// /// [`read`]: #method.read - #[cfg(any(adc_v3, adc_g0))] + #[cfg(any(adc_v3, adc_g0, adc_u0))] pub fn into_ring_buffered<'a>( &mut self, dma: Peri<'a, impl RxDma>, diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index a5b7b0c5e9..7a9200edd8 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; use {defmt_rtt as _, panic_probe as _}; const DMA_BUF_LEN: usize = 512; From 82158642e202cffbc527d672cdc33930bef5c78d Mon Sep 17 00:00:00 2001 From: Maor Malka Date: Sat, 1 Nov 2025 20:14:25 -0400 Subject: [PATCH 14/14] stm32/adc/ringbuffered_v3: lower visability for RingBufferedAdc::new --- embassy-stm32/src/adc/ringbuffered_v3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs index b58630585f..0aee309e3c 100644 --- a/embassy-stm32/src/adc/ringbuffered_v3.rs +++ b/embassy-stm32/src/adc/ringbuffered_v3.rs @@ -16,7 +16,7 @@ pub struct RingBufferedAdc<'d, T: Instance> { } impl<'d, T: Instance> RingBufferedAdc<'d, T> { - pub fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { + pub(crate) fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { //dma side setup let opts = TransferOptions { half_transfer_ir: true,