From 7ba1eeffbb111630bd40842272cfffe82f117a51 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 3 Nov 2025 14:35:13 -0600 Subject: stm32/adc: update v2 iface to match v3 --- embassy-stm32/src/adc/ringbuffered_v2.rs | 139 ++--------------------------- embassy-stm32/src/adc/v2.rs | 145 ++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 135 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 9b2e5b8fe..ecb8ae3f2 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -1,13 +1,10 @@ use core::marker::PhantomData; -use core::mem; use core::sync::atomic::{Ordering, compiler_fence}; -use stm32_metapac::adc::vals::SampleTime; - -use crate::adc::{Adc, AdcChannel, Instance, RxDma}; -use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; +use crate::adc::Instance; +use crate::dma::ReadableRingBuffer; use crate::pac::adc::vals; -use crate::{Peri, rcc}; +use crate::rcc; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OverrunError; @@ -20,7 +17,7 @@ fn clear_interrupt_flags(r: crate::pac::adc::Adc) { } #[derive(PartialOrd, PartialEq, Debug, Clone, Copy)] -pub enum Sequence { +pub(super) enum Sequence { One, Two, Three, @@ -87,44 +84,8 @@ impl From for Sequence { } pub struct RingBufferedAdc<'d, T: Instance> { - _phantom: PhantomData, - ring_buf: ReadableRingBuffer<'d, u16>, -} - -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(self, dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> 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) }; - - // Don't disable the clock - mem::forget(self); - - RingBufferedAdc { - _phantom: PhantomData, - ring_buf, - } - } + pub(super) _phantom: PhantomData, + pub(super) ring_buf: ReadableRingBuffer<'d, u16>, } impl<'d, T: Instance> RingBufferedAdc<'d, T> { @@ -132,94 +93,6 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { T::regs().cr2().read().adon() } - fn stop_adc() { - T::regs().cr2().modify(|reg| { - reg.set_adon(false); - }); - } - - fn start_adc() { - T::regs().cr2().modify(|reg| { - reg.set_adon(true); - }); - } - - /// Sets the channel sample time - /// - /// ## SAFETY: - /// - ADON == 0 i.e ADC must not be enabled when this is called. - unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { - if ch <= 9 { - T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); - } else { - T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); - } - } - - fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) { - let ch_iter = ch.iter(); - for idx in ch_iter { - unsafe { - Self::set_channel_sample_time(*idx, sample_time); - } - } - } - - pub fn set_sample_sequence( - &mut self, - sequence: Sequence, - channel: &mut impl AdcChannel, - sample_time: SampleTime, - ) { - let was_on = Self::is_on(); - if !was_on { - Self::start_adc(); - } - - // Check the sequence is long enough - T::regs().sqr1().modify(|r| { - let prev: Sequence = r.l().into(); - if prev < sequence { - let new_l: Sequence = sequence; - trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8); - r.set_l(sequence.into()) - } else { - r.set_l(prev.into()) - } - }); - - // Set this GPIO as an analog input. - channel.setup(); - - // Set the channel in the right sequence field. - match sequence { - Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())), - Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())), - Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())), - Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), - Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), - Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), - Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())), - Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())), - Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())), - Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())), - Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())), - Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())), - Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())), - Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())), - Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())), - Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())), - }; - - if !was_on { - Self::stop_adc(); - } - - self.set_channels_sample_time(&[channel.channel()], sample_time); - - Self::start_adc(); - } - /// 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(); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 93ec78548..e81e820e3 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,11 +1,16 @@ +use core::marker::PhantomData; +use core::mem; + use super::blocking_delay_us; -use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; +use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::{Peri, rcc}; +use ringbuffered_v2::Sequence; mod ringbuffered_v2; -pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; +pub use ringbuffered_v2::RingBufferedAdc; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -112,6 +117,142 @@ where } } + /// 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<'d, impl RxDma>, + dma_buf: &'d mut [u16], + sequence: impl ExactSizeIterator, SampleTime)>, + ) -> 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) }; + + for (i, (channel, sample_time)) in sequence.enumerate() { + let sequence = match i { + 0 => Sequence::One, + 1 => Sequence::Two, + 2 => Sequence::Three, + 3 => Sequence::Four, + 4 => Sequence::Five, + 5 => Sequence::Six, + 6 => Sequence::Seven, + 7 => Sequence::Eight, + 8 => Sequence::Nine, + 9 => Sequence::Ten, + 10 => Sequence::Eleven, + 11 => Sequence::Twelve, + 12 => Sequence::Thirteen, + 13 => Sequence::Fourteen, + 14 => Sequence::Fifteen, + 15 => Sequence::Sixteen, + _ => unreachable!(), + }; + + self.set_sample_sequence(sequence, channel, sample_time); + } + + // Don't disable the clock + mem::forget(self); + + RingBufferedAdc { + _phantom: PhantomData, + ring_buf, + } + } + + fn is_on() -> bool { + T::regs().cr2().read().adon() + } + + fn stop_adc() { + T::regs().cr2().modify(|reg| { + reg.set_adon(false); + }); + } + + fn start_adc() { + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + }); + } + + fn set_sample_sequence(&mut self, sequence: Sequence, channel: &mut impl AdcChannel, sample_time: SampleTime) { + let was_on = Self::is_on(); + if !was_on { + Self::start_adc(); + } + + // Check the sequence is long enough + T::regs().sqr1().modify(|r| { + let prev: Sequence = r.l().into(); + if prev < sequence { + let new_l: Sequence = sequence; + trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8); + r.set_l(sequence.into()) + } else { + r.set_l(prev.into()) + } + }); + + // Set this GPIO as an analog input. + channel.setup(); + + // Set the channel in the right sequence field. + match sequence { + Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())), + Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())), + Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())), + Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), + Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), + Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), + Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())), + Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())), + Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())), + Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())), + Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())), + Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())), + Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())), + Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())), + Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())), + Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())), + }; + + if !was_on { + Self::stop_adc(); + } + + self.set_channels_sample_time(&[channel.channel()], sample_time); + + Self::start_adc(); + } + + fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) { + let ch_iter = ch.iter(); + for idx in ch_iter { + Self::set_channel_sample_time(*idx, sample_time); + } + } + pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } -- cgit