From 1a3da1c582b4a9fec1af561e2f4a7ed0352aff33 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 09:33:27 -0600 Subject: adc: extract v2 psc. --- embassy-stm32/build.rs | 2 +- embassy-stm32/src/adc/v2.rs | 51 +++++++++++++++------------------------------ 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 67041a9e0..1e11eb8dc 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1707,7 +1707,7 @@ fn main() { for e in psc_enums.iter() { fn is_adc_name(e: &str) -> bool { match e { - "Presc" | "Adc4Presc" => true, + "Presc" | "Adc4Presc" | "Adcpre" => true, _ => false, } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 4065f89a7..07eaebf7c 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -3,6 +3,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::pac::adc::vals; +pub use crate::pac::adccommon::vals::Adcpre; use crate::time::Hertz; use crate::{Peri, rcc}; @@ -50,38 +51,20 @@ impl Temperature { } } -enum Prescaler { - Div2, - Div4, - Div6, - Div8, -} - -impl Prescaler { - fn from_pclk2(freq: Hertz) -> Self { - // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). - #[cfg(stm32f2)] - const MAX_FREQUENCY: Hertz = Hertz(30_000_000); - // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. - #[cfg(not(stm32f2))] - const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - let raw_div = freq.0 / MAX_FREQUENCY.0; - match raw_div { - 0..=1 => Self::Div2, - 2..=3 => Self::Div4, - 4..=5 => Self::Div6, - 6..=7 => Self::Div8, - _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), - } - } - - fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { - match self { - Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, - Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, - Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, - Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, - } +fn from_pclk2(freq: Hertz) -> Adcpre { + // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). + #[cfg(stm32f2)] + const MAX_FREQUENCY: Hertz = Hertz(30_000_000); + // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. + #[cfg(not(stm32f2))] + const MAX_FREQUENCY: Hertz = Hertz(36_000_000); + let raw_div = freq.0 / MAX_FREQUENCY.0; + match raw_div { + 0..=1 => Adcpre::DIV2, + 2..=3 => Adcpre::DIV4, + 4..=5 => Adcpre::DIV6, + 6..=7 => Adcpre::DIV8, + _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), } } @@ -224,8 +207,8 @@ where pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); - let presc = Prescaler::from_pclk2(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); + let presc = from_pclk2(T::frequency()); + T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); T::regs().cr2().modify(|reg| { reg.set_adon(true); }); -- cgit From a8de2ccb8c0721284281715ce6eda28271db3950 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 10:26:56 -0600 Subject: remove allow dead_code --- embassy-stm32/src/adc/adc4.rs | 1 + embassy-stm32/src/adc/mod.rs | 14 +++++++------- embassy-stm32/src/adc/v3.rs | 1 + embassy-stm32/src/adc/v4.rs | 1 + 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index d816eea57..babdebfdb 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -158,6 +158,7 @@ foreach_adc!( reg.set_chselrmod(Chselrmod::ENABLE_INPUT) }); } + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] _ => unreachable!(), } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index c91d68e87..fd5b94224 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -88,19 +88,17 @@ pub(crate) trait SealedAdcChannel { } // Temporary patch for ADCs that have not implemented the standard iface yet -#[cfg(not(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4)))] +#[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1, adc_c0))] 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))] -#[allow(dead_code)] 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))] -#[allow(dead_code)] pub(self) trait SealedAnyInstance: BasicAnyInstance { fn enable(); fn start(); @@ -108,6 +106,7 @@ pub(self) trait SealedAnyInstance: BasicAnyInstance { fn convert() -> u16; fn configure_dma(conversion_mode: ConversionMode); fn configure_sequence(sequence: impl ExactSizeIterator); + #[allow(dead_code)] fn dr() -> *mut u16; } @@ -169,17 +168,18 @@ pub enum Averaging { } #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] -#[allow(dead_code)] pub(crate) enum ConversionMode { + // Should match the cfg on "read" below #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] Singular, - #[allow(dead_code)] + // Should match the cfg on "into_ring_buffered" below + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] Repeated(RegularConversionMode), } -#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] +// Should match the cfg on "into_ring_buffered" below +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] // Conversion mode for regular ADC channels -#[allow(dead_code)] #[derive(Copy, Clone)] pub enum RegularConversionMode { // Samples as fast as possible diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index fa191c663..78b497727 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -259,6 +259,7 @@ impl super::SealedAnyInstance for T { reg.set_cont(true); reg.set_dmacfg(match conversion_mode { ConversionMode::Singular => Dmacfg::ONE_SHOT, + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, }); reg.set_dmaen(true); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 5c4a1975f..804e63db6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -143,6 +143,7 @@ impl super::SealedAnyInstance for T { reg.set_dmngt(Dmngt::DMA_ONE_SHOT); }); } + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] _ => unreachable!(), } } -- cgit From 32408f4a031dff11c1c3c8c4aeb2044f1a7e8f42 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 12:02:38 -0600 Subject: adc: extract c0 --- embassy-stm32/src/adc/c0.rs | 488 ++++++++++++++-------------------------- embassy-stm32/src/adc/mod.rs | 53 ++++- examples/stm32c0/src/bin/adc.rs | 18 +- 3 files changed, 214 insertions(+), 345 deletions(-) diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 8992d6e6e..983e7c10d 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -1,12 +1,10 @@ -use pac::adc::vals::Scandir; #[allow(unused)] use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; use pac::adccommon::vals::Presc; +use stm32_metapac::adc::vals::Scandir; -use super::{ - Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, -}; -use crate::dma::Transfer; +use super::{Adc, Instance, Resolution, blocking_delay_us}; +use crate::adc::{AnyInstance, ConversionMode}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -32,104 +30,184 @@ impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 9; } -#[derive(Copy, Clone, Debug)] -pub enum Prescaler { - NotDivided, - DividedBy2, - DividedBy4, - DividedBy6, - DividedBy8, - DividedBy10, - DividedBy12, - DividedBy16, - DividedBy32, - DividedBy64, - DividedBy128, - DividedBy256, +fn from_ker_ck(frequency: Hertz) -> Presc { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Presc::DIV1, + 1 => Presc::DIV2, + 2..=3 => Presc::DIV4, + 4..=5 => Presc::DIV6, + 6..=7 => Presc::DIV8, + 8..=9 => Presc::DIV10, + 10..=11 => Presc::DIV12, + _ => unimplemented!(), + } } -impl Prescaler { - fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; - match raw_prescaler { - 0 => Self::NotDivided, - 1 => Self::DividedBy2, - 2..=3 => Self::DividedBy4, - 4..=5 => Self::DividedBy6, - 6..=7 => Self::DividedBy8, - 8..=9 => Self::DividedBy10, - 10..=11 => Self::DividedBy12, - _ => unimplemented!(), - } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - #[allow(unused)] - fn divisor(&self) -> u32 { - match self { - Prescaler::NotDivided => 1, - Prescaler::DividedBy2 => 2, - Prescaler::DividedBy4 => 4, - Prescaler::DividedBy6 => 6, - Prescaler::DividedBy8 => 8, - Prescaler::DividedBy10 => 10, - Prescaler::DividedBy12 => 12, - Prescaler::DividedBy16 => 16, - Prescaler::DividedBy32 => 32, - Prescaler::DividedBy64 => 64, - Prescaler::DividedBy128 => 128, - Prescaler::DividedBy256 => 256, + fn enable() { + T::regs().isr().modify(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + // ADRDY is "ADC ready". Wait until it will be True. + while !T::regs().isr().read().adrdy() {} + } + + fn start() { + // Start conversion + T::regs().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| { + reg.set_adstp(Adstp::STOP); + }); + while T::regs().cr().read().adstart() {} } + + // Reset configuration. + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + reg.set_dmacfg(Dmacfg::from_bits(0)); + reg.set_dmaen(false); + }); } - fn presc(&self) -> Presc { - match self { - Prescaler::NotDivided => Presc::DIV1, - Prescaler::DividedBy2 => Presc::DIV2, - Prescaler::DividedBy4 => Presc::DIV4, - Prescaler::DividedBy6 => Presc::DIV6, - Prescaler::DividedBy8 => Presc::DIV8, - Prescaler::DividedBy10 => Presc::DIV10, - Prescaler::DividedBy12 => Presc::DIV12, - Prescaler::DividedBy16 => Presc::DIV16, - Prescaler::DividedBy32 => Presc::DIV32, - Prescaler::DividedBy64 => Presc::DIV64, - Prescaler::DividedBy128 => Presc::DIV128, - Prescaler::DividedBy256 => Presc::DIV256, + fn configure_dma(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| { + reg.set_ovr(true); + }); + + // Set continuous mode with oneshot dma. + T::regs().cfgr1().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); + reg.set_dmaen(true); + reg.set_ovrmod(Ovrmod::PRESERVE); + }); + } } } -} -#[cfg(feature = "defmt")] -impl<'a> defmt::Format for Prescaler { - fn format(&self, fmt: defmt::Formatter) { - match self { - Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), - Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), - Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), - Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), - Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), - Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), - Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), - Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), - Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), - Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), - Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), - Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), + fn configure_sequence(mut sequence: impl ExactSizeIterator, blocking: bool) { + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(!blocking); + reg.set_align(Align::RIGHT); + }); + + assert!(!blocking || sequence.len() == 1, "Sequence len must be 1 for blocking."); + if blocking { + let ((ch, _), sample_time) = sequence.next().unwrap(); + // Set all channels to use SMP1 field as source. + T::regs().smpr().modify(|w| { + w.smpsel(0); + w.set_smp1(sample_time); + }); + + // write() because we want all other bits to be set to 0. + T::regs().chselr().write(|w| w.set_chsel(ch.into(), true)); + } else { + let mut hw_channel_selection: u32 = 0; + let mut is_ordered_up = true; + let mut is_ordered_down = true; + let mut needs_hw = false; + + assert!( + sequence.len() <= CHSELR_SQ_SIZE, + "Sequence read set cannot be more than {} in size.", + CHSELR_SQ_SIZE + ); + let mut last_sq_set: usize = 0; + let mut last_channel: u8 = 0; + T::regs().chselr_sq().write(|w| { + for (i, ((channel, _), _sample_time)) in sequence.enumerate() { + needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; + last_sq_set = i; + is_ordered_up = is_ordered_up && channel > last_channel; + is_ordered_down = is_ordered_down && channel < last_channel; + hw_channel_selection += 1 << channel; + last_channel = channel; + + if !needs_hw { + w.set_sq(i, channel); + } + } + + assert!( + !needs_hw || is_ordered_up || is_ordered_down, + "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", + CHSELR_SQ_MAX_CHANNEL + ); + + if needs_hw { + assert!( + hw_channel_selection != 0, + "Some bits in `hw_channel_selection` shall be set." + ); + assert!( + (hw_channel_selection >> NUM_HW_CHANNELS) == 0, + "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", + NUM_HW_CHANNELS + ); + + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(false); + reg.set_scandir(if is_ordered_up { Scandir::UP} else { Scandir::BACK }); + }); + + // Set required channels for multi-convert. + unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } + } else { + for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { + w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); + } + } + }); } + + // 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() {} + } + + fn convert() -> u16 { + // Set single conversion mode. + T::regs().cfgr1().modify(|w| w.set_cont(false)); + + // Start conversion + T::regs().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 } } -impl<'d, T: Instance> Adc<'d, T> { +impl<'d, T: AnyInstance> Adc<'d, T> { /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { rcc::enable_and_reset::(); T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); - let prescaler = Prescaler::from_ker_ck(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + let prescaler = from_ker_ck(T::frequency()); + T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + let frequency = T::frequency() / prescaler; debug!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { @@ -139,22 +217,6 @@ impl<'d, T: Instance> Adc<'d, T> { ); } - let mut s = Self { adc }; - - s.power_up(); - - s.set_resolution(resolution); - - s.calibrate(); - - s.enable(); - - s.configure_default(); - - s - } - - fn power_up(&mut self) { T::regs().cr().modify(|reg| { reg.set_advregen(true); }); @@ -162,9 +224,9 @@ impl<'d, T: Instance> Adc<'d, T> { // "The software must wait for the ADC voltage regulator startup time." // See datasheet for the value. blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); - } - fn calibrate(&mut self) { + T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); + // We have to make sure AUTOFF is OFF, but keep its value after calibration. let autoff_value = T::regs().cfgr1().read().autoff(); T::regs().cfgr1().modify(|w| w.set_autoff(false)); @@ -178,22 +240,17 @@ impl<'d, T: Instance> Adc<'d, T> { debug!("ADC calibration value: {}.", T::regs().dr().read().data()); T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); - } - fn enable(&mut self) { - T::regs().isr().modify(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - // ADRDY is "ADC ready". Wait until it will be True. - while !T::regs().isr().read().adrdy() {} - } + T::enable(); - fn configure_default(&mut self) { // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { w.set_cont(false); w.set_exten(Exten::DISABLED); w.set_align(Align::RIGHT); }); + + Self { adc } } /// Enable reading the voltage reference internal channel. @@ -214,219 +271,4 @@ impl<'d, T: Instance> Adc<'d, T> { super::Temperature {} } - - /// Set the ADC sample time. - /// Shall only be called when ADC is not converting. - pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { - // Set all channels to use SMP1 field as source. - T::regs().smpr().modify(|w| { - w.smpsel(0); - w.set_smp1(sample_time); - }); - } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); - } - - /// Perform a single conversion. - fn convert(&mut self) -> u16 { - // Set single conversion mode. - T::regs().cfgr1().modify(|w| w.set_cont(false)); - - // Start conversion - T::regs().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 - } - - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.set_sample_time_all_channels(sample_time); - - Self::configure_channel(channel); - T::regs().cfgr1().write(|reg| { - reg.set_chselrmod(false); - reg.set_align(Align::RIGHT); - }); - self.convert() - } - - fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator>) { - assert!( - channel_sequence.len() <= CHSELR_SQ_SIZE, - "Seqenced read set cannot be more than {} in size.", - CHSELR_SQ_SIZE - ); - let mut last_sq_set: usize = 0; - T::regs().chselr_sq().write(|w| { - for (i, channel) in channel_sequence.enumerate() { - assert!( - channel.channel() <= CHSELR_SQ_MAX_CHANNEL, - "Sequencer only support HW channels smaller than {}.", - CHSELR_SQ_MAX_CHANNEL - ); - w.set_sq(i, channel.channel()); - last_sq_set = i; - } - - for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { - w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); - } - }); - - Self::apply_channel_conf() - } - - async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma>, readings: &mut [u16]) { - // Enable overrun control, so no new DMA requests will be generated until - // previous DR values is read. - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - }); - - // Set continuous mode with oneshot dma. - T::regs().cfgr1().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); - reg.set_dmaen(true); - reg.set_ovrmod(Ovrmod::PRESERVE); - }); - - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion. - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - // Wait for conversion sequence to finish. - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - T::regs().cfgr1().modify(|reg| { - reg.set_cont(false); - reg.set_dmacfg(Dmacfg::from_bits(0)); - reg.set_dmaen(false); - }); - } - - /// Read one or multiple ADC channels using DMA in hardware order. - /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. - /// Readings won't be in the same order as in the `set`! - /// - /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use - /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). - /// TODO(chudsaviet): externalize generic code and merge with read(). - pub async fn read_in_hw_order( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - hw_channel_selection: u32, - scandir: Scandir, - readings: &mut [u16], - ) { - assert!( - hw_channel_selection != 0, - "Some bits in `hw_channel_selection` shall be set." - ); - assert!( - (hw_channel_selection >> NUM_HW_CHANNELS) == 0, - "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", - NUM_HW_CHANNELS - ); - // To check for correct readings slice size, we shall solve Hamming weight problem, - // which is either slow or memory consuming. - // Since we have limited resources, we don't do it here. - // Not doing this have a great potential for a bug through. - - // Ensure no conversions are ongoing. - Self::cancel_conversions(); - - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(false); - reg.set_scandir(scandir); - reg.set_align(Align::RIGHT); - }); - - // Set required channels for multi-convert. - unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } - - Self::apply_channel_conf(); - - self.dma_convert(rx_dma, readings).await - } - - // Read ADC channels in specified order using DMA (CHSELRMOD = 1). - // In STM32C0, only lower 14 ADC channels can be read this way. - // For other channels, use `read_in_hw_order()` or blocking read. - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - channel_sequence: impl ExactSizeIterator>, - readings: &mut [u16], - ) { - assert!( - channel_sequence.len() != 0, - "Asynchronous read channel sequence cannot be empty." - ); - assert!( - channel_sequence.len() == readings.len(), - "Channel sequence length must be equal to readings length." - ); - - // Ensure no conversions are ongoing. - Self::cancel_conversions(); - - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(true); - reg.set_align(Align::RIGHT); - }); - - Self::setup_channel_sequencer(channel_sequence); - - self.dma_convert(rx_dma, readings).await - } - - fn configure_channel(channel: &mut impl AdcChannel) { - channel.setup(); - // write() because we want all other bits to be set to 0. - T::regs() - .chselr() - .write(|w| w.set_chsel(channel.channel().into(), true)); - - Self::apply_channel_conf(); - } - - fn apply_channel_conf() { - // 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() {} - } - - fn cancel_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(Adstp::STOP); - }); - while T::regs().cr().read().adstart() {} - } - } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index fd5b94224..549f2f5a5 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -88,30 +88,37 @@ 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, adc_c0))] +#[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))] +#[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))] +#[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); + #[cfg(not(adc_c0))] fn configure_sequence(sequence: impl ExactSizeIterator); + #[cfg(adc_c0)] + fn configure_sequence(sequence: impl ExactSizeIterator, blocking: bool); #[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))] +#[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 {} @@ -121,12 +128,16 @@ pub trait AnyInstance: SealedAnyInstance + Instance {} 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))] +#[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))] +#[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 {} /// Performs a busy-wait delay for a specified number of microseconds. @@ -167,10 +178,12 @@ pub enum Averaging { Samples1024, } -#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] +#[cfg(any( + adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 +))] pub(crate) enum ConversionMode { // Should match the cfg on "read" below - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] Singular, // Should match the cfg on "into_ring_buffered" below #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] @@ -190,7 +203,9 @@ 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))] + #[cfg(any( + adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, 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))] @@ -198,12 +213,18 @@ impl<'d, T: AnyInstance> Adc<'d, T> { #[cfg(not(adc_v4))] T::enable(); + #[cfg(not(adc_c0))] T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + #[cfg(adc_c0)] + T::configure_sequence( + [((channel.channel(), channel.is_differential()), sample_time)].into_iter(), + true, + ); T::convert() } - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] /// Read one or multiple ADC regular channels using DMA. /// /// `sequence` iterator and `readings` must have the same length. @@ -232,6 +253,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { /// /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use /// `into_ring_buffered`, `into_ring_buffered_and_injected` + /// + /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use + /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). + /// + /// In addtion, on STM320, this method will panic if the channels are not passed in order pub async fn read( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, @@ -252,8 +278,15 @@ impl<'d, T: AnyInstance> Adc<'d, T> { T::stop(); T::enable(); + #[cfg(not(adc_c0))] + T::configure_sequence( + sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + ); + + #[cfg(adc_c0)] T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + false, ); T::configure_dma(ConversionMode::Singular); diff --git a/examples/stm32c0/src/bin/adc.rs b/examples/stm32c0/src/bin/adc.rs index b52c9e7f8..ad597b63c 100644 --- a/examples/stm32c0/src/bin/adc.rs +++ b/examples/stm32c0/src/bin/adc.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::vals::Scandir; use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; use embassy_stm32::peripherals::ADC1; use embassy_time::Timer; @@ -35,8 +34,12 @@ async fn main(_spawner: Spawner) { blocking_vref, blocking_temp, blocing_pin0 ); - let channels_seqence: [&mut AnyAdcChannel; 3] = [&mut vref, &mut temp, &mut pin0]; - adc.read(dma.reborrow(), channels_seqence.into_iter(), &mut read_buffer) + let channels_sequence: [(&mut AnyAdcChannel, SampleTime); 3] = [ + (&mut vref, SampleTime::CYCLES12_5), + (&mut temp, SampleTime::CYCLES12_5), + (&mut pin0, SampleTime::CYCLES12_5), + ]; + adc.read(dma.reborrow(), channels_sequence.into_iter(), &mut read_buffer) .await; // Values are ordered according to hardware ADC channel number! info!( @@ -44,15 +47,6 @@ async fn main(_spawner: Spawner) { read_buffer[0], read_buffer[1], read_buffer[2] ); - let hw_channel_selection: u32 = - (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); - adc.read_in_hw_order(dma.reborrow(), hw_channel_selection, Scandir::UP, &mut read_buffer) - .await; - info!( - "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", - read_buffer[2], read_buffer[1], read_buffer[0] - ); - Timer::after_millis(2000).await; } } -- cgit