From e32f78fde6f8130f1eb3effa131e42b7ca153ba6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Nov 2025 13:14:15 -0600 Subject: stm32/adc: extract into common add common low-level interface for adc --- embassy-stm32/src/adc/adc4.rs | 110 +-- embassy-stm32/src/adc/g4.rs | 459 ++++--------- embassy-stm32/src/adc/injected.rs | 2 +- embassy-stm32/src/adc/mod.rs | 173 +++++ embassy-stm32/src/adc/ringbuffered.rs | 2 +- embassy-stm32/src/adc/v2.rs | 233 +++---- embassy-stm32/src/adc/v3.rs | 738 +++++++-------------- embassy-stm32/src/adc/v4.rs | 327 ++++----- examples/stm32f4/src/bin/adc.rs | 2 +- examples/stm32f4/src/bin/adc_dma.rs | 8 +- examples/stm32g0/src/bin/adc_oversampling.rs | 14 +- examples/stm32g4/src/bin/adc.rs | 4 +- examples/stm32g4/src/bin/adc_differential.rs | 2 +- examples/stm32g4/src/bin/adc_dma.rs | 2 +- .../stm32g4/src/bin/adc_injected_and_regular.rs | 2 +- examples/stm32g4/src/bin/adc_oversampling.rs | 13 +- examples/stm32l4/src/bin/adc.rs | 9 +- examples/stm32l4/src/bin/adc_dma.rs | 5 +- examples/stm32u0/src/bin/adc.rs | 7 +- examples/stm32u5/src/bin/adc.rs | 16 +- 20 files changed, 869 insertions(+), 1259 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index befa8ed4a..04d976513 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -4,7 +4,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR #[cfg(stm32wba)] use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; -use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; +use super::{AdcChannel, AnyAdcChannel, RxDma4, blocking_delay_us}; use crate::dma::Transfer; #[cfg(stm32u5)] pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; @@ -24,56 +24,24 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -const VREF_CHANNEL: u8 = 0; -const VCORE_CHANNEL: u8 = 12; -const TEMP_CHANNEL: u8 = 13; -const VBAT_CHANNEL: u8 = 14; -const DAC_CHANNEL: u8 = 21; - -// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs -/// Internal voltage reference channel. -pub struct VrefInt; -impl AdcChannel for VrefInt {} -impl SealedAdcChannel for VrefInt { - fn channel(&self) -> u8 { - VREF_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 0; } -/// Internal temperature channel. -pub struct Temperature; -impl AdcChannel for Temperature {} -impl SealedAdcChannel for Temperature { - fn channel(&self) -> u8 { - TEMP_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 13; } -/// Internal battery voltage channel. -pub struct Vbat; -impl AdcChannel for Vbat {} -impl SealedAdcChannel for Vbat { - fn channel(&self) -> u8 { - VBAT_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 12; } -/// Internal DAC channel. -pub struct Dac; -impl AdcChannel for Dac {} -impl SealedAdcChannel for Dac { - fn channel(&self) -> u8 { - DAC_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 14; } -/// Internal Vcore channel. -pub struct Vcore; -impl AdcChannel for Vcore {} -impl SealedAdcChannel for Vcore { - fn channel(&self) -> u8 { - VCORE_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 21; } #[derive(Copy, Clone)] @@ -214,20 +182,6 @@ impl<'d, T: Instance> Adc4<'d, T> { ); } - let mut s = Self { adc }; - - s.power_up(); - - s.calibrate(); - blocking_delay_us(1); - - s.enable(); - s.configure(); - - s - } - - fn power_up(&mut self) { T::regs().isr().modify(|w| { w.set_ldordy(true); }); @@ -239,22 +193,15 @@ impl<'d, T: Instance> Adc4<'d, T> { T::regs().isr().modify(|w| { w.set_ldordy(true); }); - } - fn calibrate(&mut self) { T::regs().cr().modify(|w| w.set_adcal(true)); while T::regs().cr().read().adcal() {} T::regs().isr().modify(|w| w.set_eocal(true)); - } - fn enable(&mut self) { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } + blocking_delay_us(1); + + Self::enable(); - fn configure(&mut self) { // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { #[cfg(stm32u5)] @@ -280,51 +227,60 @@ impl<'d, T: Instance> Adc4<'d, T> { w.set_smpsel(i, Smpsel::SMP1); } }); + + Self { adc } + } + + fn enable() { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); } /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> VrefInt { + pub fn enable_vrefint(&self) -> super::VrefInt { T::regs().ccr().modify(|w| { w.set_vrefen(true); }); - VrefInt {} + super::VrefInt {} } /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> Temperature { + pub fn enable_temperature(&self) -> super::Temperature { T::regs().ccr().modify(|w| { w.set_vsensesel(true); }); - Temperature {} + super::Temperature {} } /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_vbat(&self) -> Vbat { + pub fn enable_vbat(&self) -> super::Vbat { T::regs().ccr().modify(|w| { w.set_vbaten(true); }); - Vbat {} + super::Vbat {} } /// Enable reading the vbat internal channel. - pub fn enable_vcore(&self) -> Vcore { - Vcore {} + pub fn enable_vcore(&self) -> super::Vcore { + super::Vcore {} } /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { + pub fn enable_dac_channel(&self, dac: DacChannel) -> super::Dac { let mux; match dac { DacChannel::OUT1 => mux = false, DacChannel::OUT2 => mux = true, } T::regs().or().modify(|w| w.set_chn21sel(mux)); - Dac {} + super::Dac {} } /// Set the ADC resolution. diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 6430b0243..0a9f35a5b 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -1,5 +1,3 @@ -use core::mem; - #[allow(unused)] #[cfg(stm32h7)] use pac::adc::vals::{Adcaldif, Difsel, Exten}; @@ -10,15 +8,14 @@ pub use pac::adccommon::vals::Presc; pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; pub use stm32_metapac::adccommon::vals::Dual; -use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; +use super::{ + Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, + blocking_delay_us, +}; use crate::adc::SealedAdcChannel; -use crate::dma::Transfer; use crate::time::Hertz; use crate::{Peri, pac, rcc}; -mod ringbuffered; -pub use ringbuffered::RingBufferedAdc; - mod injected; pub use injected::InjectedAdc; @@ -103,6 +100,19 @@ impl Prescaler { } } +/// ADC configuration +#[derive(Default)] +pub struct AdcConfig { + pub dual_mode: Option, + pub resolution: Option, + #[cfg(stm32g4)] + pub oversampling_shift: Option, + #[cfg(stm32g4)] + pub oversampling_ratio: Option, + #[cfg(stm32g4)] + pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, +} + // Trigger source for ADC conversions¨ #[derive(Copy, Clone)] pub struct ConversionTrigger { @@ -112,18 +122,9 @@ pub struct ConversionTrigger { pub edge: Exten, } -// Conversion mode for regular ADC channels -#[derive(Copy, Clone)] -pub enum RegularConversionMode { - // Samples as fast as possible - Continuous, - // Sample at rate determined by external trigger - Triggered(ConversionTrigger), -} - impl<'d, T: Instance> Adc<'d, T> { /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { + pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); let prescaler = Prescaler::from_ker_ck(T::frequency()); @@ -181,10 +182,38 @@ impl<'d, T: Instance> Adc<'d, T> { w.set_exten(Exten::DISABLED); }); + if let Some(dual) = config.dual_mode { + T::common_regs().ccr().modify(|reg| { + reg.set_dual(dual); + }) + } + + if let Some(resolution) = config.resolution { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + #[cfg(stm32g4)] + if let Some(shift) = config.oversampling_shift { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + + #[cfg(stm32g4)] + if let Some(ratio) = config.oversampling_ratio { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(stm32g4)] + if let Some((mode, trig_mode, enable)) = config.oversampling_mode { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + Self { adc } } - fn enable() { + /// Enable the ADC + pub(super) fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -205,82 +234,33 @@ impl<'d, T: Instance> Adc<'d, T> { } } - /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> super::VrefInt - where - T: super::SpecialConverter, - { - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); + /// Start regular adc conversion + pub(super) fn start() { + T::regs().cr().modify(|reg| { + reg.set_adstart(true); }); - - super::VrefInt {} } - /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> super::Temperature - where - T: super::SpecialConverter, - { - T::common_regs().ccr().modify(|reg| { - reg.set_vsenseen(true); - }); - - super::Temperature {} - } + /// Stop regular conversions and disable DMA + pub(super) fn stop() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(Adstp::STOP); + }); + // The software must poll ADSTART until the bit is reset before assuming the + // ADC is completely stopped + while T::regs().cr().read().adstart() {} + } - /// Enable reading the vbat internal channel. - pub fn enable_vbat(&self) -> super::Vbat - where - T: super::SpecialConverter, - { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); + // Disable dma control and continuous conversion, if enabled + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(Dmaen::DISABLE); }); - - super::Vbat {} - } - - /// Set oversampling shift. - #[cfg(stm32g4)] - pub fn set_oversampling_shift(&mut self, shift: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - - /// Set oversampling ratio. - #[cfg(stm32g4)] - pub fn set_oversampling_ratio(&mut self, ratio: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - /// Enable oversampling in regular mode. - #[cfg(stm32g4)] - pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { - T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - } - - // Reads that are not implemented as INJECTED in "blocking_read" - // #[cfg(stm32g4)] - // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { - // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); - // } - - // #[cfg(stm32g4)] - // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { - // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), - // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); - // } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); } /// Perform a single conversion. - fn convert(&mut self) -> u16 { + pub(super) fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -298,136 +278,42 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - /// Read an ADC pin. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - channel.setup(); - - Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); - - #[cfg(stm32h7)] - { - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() - .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); - } - - self.convert() - } - - /// Start regular adc conversion - pub(super) fn start() { - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - } - - /// Stop regular conversions - pub(super) fn stop() { - Self::stop_regular_conversions(); - } - - /// Teardown method for stopping regular ADC conversions - pub(super) fn teardown_dma() { - Self::stop_regular_conversions(); - - // Disable dma control - T::regs().cfgr().modify(|reg| { - reg.set_dmaen(Dmaen::DISABLE); - }); - } - - /// Read one or multiple ADC regular channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::{Adc, AdcChannel} - /// - /// let mut adc = Adc::new(p.ADC1); - /// let mut adc_pin0 = p.PA0.into(); - /// let mut adc_pin1 = p.PA1.into(); - /// let mut measurements = [0u16; 2]; - /// - /// adc.read( - /// p.DMA1_CH2.reborrow(), - /// [ - /// (&mut *adc_pin0, SampleTime::CYCLES160_5), - /// (&mut *adc_pin1, SampleTime::CYCLES160_5), - /// ] - /// .into_iter(), - /// &mut measurements, - /// ) - /// .await; - /// defmt::info!("measurements: {}", measurements); - /// ``` - /// - /// 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` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, - readings: &mut [u16], - ) { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - - // Ensure no conversions are ongoing and ADC is enabled. - Self::stop_regular_conversions(); - Self::enable(); - - Self::configure_sequence( - sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), - ); - - // Set continuous mode with oneshot dma. - // Clear overrun flag before starting transfer. + pub(super) fn configure_dma(conversion_mode: ConversionMode) { T::regs().isr().modify(|reg| { reg.set_ovr(true); }); T::regs().cfgr().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); + reg.set_discen(false); // Convert all channels for each trigger + reg.set_dmacfg(match conversion_mode { + ConversionMode::Singular => Dmacfg::ONE_SHOT, + ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, + }); reg.set_dmaen(Dmaen::ENABLE); }); - 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; + if let ConversionMode::Repeated(mode) = conversion_mode { + match mode { + RegularConversionMode::Continuous => { + T::regs().cfgr().modify(|reg| { + reg.set_cont(true); + }); + } + RegularConversionMode::Triggered(trigger) => { + T::regs().cfgr().modify(|r| { + r.set_cont(false); // New trigger is neede for each sample to be read + }); - // Ensure conversions are finished. - Self::stop_regular_conversions(); + T::regs().cfgr().modify(|r| { + r.set_extsel(trigger.channel); + r.set_exten(trigger.edge); + }); - // Reset configuration. - T::regs().cfgr().modify(|reg| { - reg.set_cont(false); - }); + // Regular conversions uses DMA so no need to generate interrupt + T::regs().ier().modify(|r| r.set_eosie(false)); + } + } + } } pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { @@ -489,95 +375,54 @@ impl<'d, T: Instance> Adc<'d, T> { } } - /// Set external trigger for regular conversion sequence - fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { - T::regs().cfgr().modify(|r| { - r.set_extsel(trigger.channel); - r.set_exten(trigger.edge); + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> super::VrefInt + where + T: super::SpecialConverter, + { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); }); - // Regular conversions uses DMA so no need to generate interrupt - T::regs().ier().modify(|r| r.set_eosie(false)); - } - // Dual ADC mode selection - pub fn configure_dual_mode(&mut self, val: Dual) { - T::common_regs().ccr().modify(|reg| { - reg.set_dual(val); - }) + super::VrefInt {} } - /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. - /// - /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer - /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended - /// to configure `dma_buf` as a double buffer so that one half can be read while the other half - /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively - /// defines the period at which the buffer should be read. - /// - /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent - /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. - /// For example, if 3 channels are measured and you want to store 40 samples per channel, - /// the buffer length should be `3 * 40 = 120`. - /// - /// # Parameters - /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. - /// - `dma_buf`: The buffer where DMA stores ADC samples. - /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. - /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). - /// - /// # Returns - /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. - pub fn into_ring_buffered<'a>( - mut self, - dma: Peri<'a, impl RxDma>, - dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, SampleTime)>, - mode: RegularConversionMode, - ) -> 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::stop_regular_conversions(); - Self::enable(); - - //adc side setup - Self::configure_sequence( - sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), - ); - - // Clear overrun flag before starting transfer. - T::regs().isr().modify(|reg| { - reg.set_ovr(true); + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> super::Temperature + where + T: super::SpecialConverter, + { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); }); - T::regs().cfgr().modify(|reg| { - reg.set_discen(false); // Convert all channels for each trigger - reg.set_dmacfg(Dmacfg::CIRCULAR); - reg.set_dmaen(Dmaen::ENABLE); + super::Temperature {} + } + + /// Enable reading the vbat internal channel. + pub fn enable_vbat(&self) -> super::Vbat + where + T: super::SpecialConverter, + { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); }); - match mode { - RegularConversionMode::Continuous => { - T::regs().cfgr().modify(|reg| { - reg.set_cont(true); - }); - } - RegularConversionMode::Triggered(trigger) => { - T::regs().cfgr().modify(|r| { - r.set_cont(false); // New trigger is neede for each sample to be read - }); - self.set_regular_conversion_trigger(trigger); - } - } + super::Vbat {} + } - mem::forget(self); + // Reads that are not implemented as INJECTED in "blocking_read" + // #[cfg(stm32g4)] + // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { + // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); + // } - RingBufferedAdc::new(dma, dma_buf) - } + // #[cfg(stm32g4)] + // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { + // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), + // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); + // } /// Configures the ADC for injected conversions. /// @@ -607,7 +452,7 @@ impl<'d, T: Instance> Adc<'d, T> { /// - Accessing samples beyond `N` will result in a panic; use the returned type /// `InjectedAdc` to enforce bounds at compile time. pub fn setup_injected_conversions<'a, const N: usize>( - mut self, + self, sequence: [(AnyAdcChannel, SampleTime); N], trigger: ConversionTrigger, interrupt: bool, @@ -619,7 +464,7 @@ impl<'d, T: Instance> Adc<'d, T> { NR_INJECTED_RANKS ); - Self::stop_regular_conversions(); + Self::stop(); Self::enable(); T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); @@ -649,8 +494,16 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); - self.set_injected_conversion_trigger(trigger); - self.enable_injected_eos_interrupt(interrupt); + // Set external trigger for injected conversion sequence + // Possible trigger values are seen in Table 167 in RM0440 Rev 9 + T::regs().jsqr().modify(|r| { + r.set_jextsel(trigger.channel); + r.set_jexten(trigger.edge); + }); + + // Enable end of injected sequence interrupt + T::regs().ier().modify(|r| r.set_jeosie(interrupt)); + Self::start_injected_conversions(); InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels @@ -688,7 +541,7 @@ impl<'d, T: Instance> Adc<'d, T> { injected_sequence: [(AnyAdcChannel, SampleTime); N], injected_trigger: ConversionTrigger, injected_interrupt: bool, - ) -> (RingBufferedAdc<'a, T>, InjectedAdc) { + ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc) { unsafe { ( Self { @@ -721,32 +574,6 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_jadstart(true); }); } - - /// Set external trigger for injected conversion sequence - /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 - fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { - T::regs().jsqr().modify(|r| { - r.set_jextsel(trigger.channel); - r.set_jexten(trigger.edge); - }); - } - - /// Enable end of injected sequence interrupt - fn enable_injected_eos_interrupt(&mut self, enable: bool) { - T::regs().ier().modify(|r| r.set_jeosie(enable)); - } - - // Stop regular conversions - fn stop_regular_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(Adstp::STOP); - }); - // The software must poll ADSTART until the bit is reset before assuming the - // ADC is completely stopped - while T::regs().cr().read().adstart() {} - } - } } impl InjectedAdc { diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index f9f1bba2a..7bb3a541c 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs @@ -38,7 +38,7 @@ impl InjectedAdc { impl Drop for InjectedAdc { fn drop(&mut self) { - Adc::::teardown_dma(); + Adc::::stop(); compiler_fence(Ordering::SeqCst); } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e321c4fa1..bf404d6ef 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -17,6 +17,9 @@ #[cfg_attr(adc_c0, path = "c0.rs")] mod _version; +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] +mod ringbuffered; + use core::marker::PhantomData; #[allow(unused)] @@ -25,6 +28,8 @@ pub use _version::*; use embassy_hal_internal::{PeripheralType, impl_peripheral}; #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] use embassy_sync::waitqueue::AtomicWaker; +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] +pub use ringbuffered::RingBufferedAdc; #[cfg(any(adc_u5, adc_wba))] #[path = "adc4.rs"] @@ -105,6 +110,166 @@ pub(crate) fn blocking_delay_us(us: u32) { } } +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +pub(self) enum ConversionMode { + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] + Singular, + #[allow(dead_code)] + Repeated(RegularConversionMode), +} + +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +// Conversion mode for regular ADC channels +#[derive(Copy, Clone)] +pub enum RegularConversionMode { + // Samples as fast as possible + Continuous, + #[cfg(adc_g4)] + // Sample at rate determined by external trigger + Triggered(ConversionTrigger), +} + +impl<'d, T: Instance> Adc<'d, T> { + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4))] + /// Read an ADC pin. + pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + channel.setup(); + + #[cfg(not(adc_v4))] + Self::enable(); + Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + + Self::convert() + } + + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] + /// Read one or multiple ADC regular channels using DMA. + /// + /// `sequence` iterator and `readings` must have the same length. + /// + /// Example + /// ```rust,ignore + /// use embassy_stm32::adc::{Adc, AdcChannel} + /// + /// let mut adc = Adc::new(p.ADC1); + /// let mut adc_pin0 = p.PA0.into(); + /// let mut adc_pin1 = p.PA1.into(); + /// let mut measurements = [0u16; 2]; + /// + /// adc.read( + /// p.DMA1_CH2.reborrow(), + /// [ + /// (&mut *adc_pin0, SampleTime::CYCLES160_5), + /// (&mut *adc_pin1, SampleTime::CYCLES160_5), + /// ] + /// .into_iter(), + /// &mut measurements, + /// ) + /// .await; + /// defmt::info!("measurements: {}", measurements); + /// ``` + /// + /// 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` + pub async fn read( + &mut self, + rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, + sequence: impl ExactSizeIterator, SampleTime)>, + readings: &mut [u16], + ) { + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() == readings.len(), + "Sequence length must be equal to readings length" + ); + assert!( + sequence.len() <= 16, + "Asynchronous read sequence cannot be more than 16 in length" + ); + + // Ensure no conversions are ongoing and ADC is enabled. + Self::stop(); + Self::enable(); + + Self::configure_sequence( + sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + ); + + Self::configure_dma(ConversionMode::Singular); + + let request = rx_dma.request(); + let transfer = unsafe { + crate::dma::Transfer::new_read( + rx_dma, + request, + T::regs().dr().as_ptr() as *mut u16, + readings, + Default::default(), + ) + }; + + Self::start(); + + // Wait for conversion sequence to finish. + transfer.await; + + // Ensure conversions are finished. + Self::stop(); + } + + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] + /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. + /// + /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer + /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended + /// to configure `dma_buf` as a double buffer so that one half can be read while the other half + /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively + /// defines the period at which the buffer should be read. + /// + /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent + /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. + /// For example, if 3 channels are measured and you want to store 40 samples per channel, + /// the buffer length should be `3 * 40 = 120`. + /// + /// # Parameters + /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. + /// - `dma_buf`: The buffer where DMA stores ADC samples. + /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. + /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). + /// + /// # Returns + /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. + pub fn into_ring_buffered<'a>( + self, + dma: embassy_hal_internal::Peri<'a, impl RxDma>, + dma_buf: &'a mut [u16], + sequence: impl ExactSizeIterator, SampleTime)>, + mode: RegularConversionMode, + ) -> 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::stop(); + Self::enable(); + + //adc side setup + Self::configure_sequence( + sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + ); + + Self::configure_dma(ConversionMode::Repeated(mode)); + + core::mem::forget(self); + + RingBufferedAdc::new(dma, dma_buf) + } +} + pub(self) trait SpecialChannel {} /// Implemented for ADCs that have a special channel @@ -143,6 +308,14 @@ impl SpecialChannel for Temperature {} pub struct Vbat; impl SpecialChannel for Vbat {} +/// Vcore channel. +pub struct Vcore; +impl SpecialChannel for Vcore {} + +/// Internal dac channel. +pub struct Dac; +impl SpecialChannel for Dac {} + /// ADC instance. #[cfg(not(any( adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 024c6acdc..62ea0d3a2 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -172,7 +172,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { impl Drop for RingBufferedAdc<'_, T> { fn drop(&mut self) { - Adc::::teardown_dma(); + Adc::::stop(); compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index efa1cc68c..2f9fabafb 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,15 +1,11 @@ -use core::mem; use core::sync::atomic::{Ordering, compiler_fence}; -use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; -use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; +use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; +use crate::adc::{Adc, Instance, Resolution, SampleTime}; use crate::pac::adc::vals; use crate::time::Hertz; 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); @@ -89,11 +85,21 @@ impl Prescaler { } } +/// ADC configuration +#[derive(Default)] +pub struct AdcConfig { + resolution: Option, +} + impl<'d, T> Adc<'d, T> where T: Instance, { pub fn new(adc: Peri<'d, T>) -> Self { + Self::new_with_config(adc, Default::default()) + } + + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); let presc = Prescaler::from_pclk2(T::frequency()); @@ -104,84 +110,14 @@ where blocking_delay_us(3); - Self { adc } - } - - /// 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>( - 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); - - Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { - channel.setup(); - - (channel.channel, sample_time) - })); - compiler_fence(Ordering::SeqCst); - - Self::setup_dma(); - - // Don't disable the clock - mem::forget(self); - - RingBufferedAdc::new(dma, dma_buf) - } - - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - channel.setup(); - - // Configure ADC - let channel = channel.channel(); - - Self::configure_sequence([(channel, sample_time)].into_iter()); - Self::blocking_convert() - } - - /// Enables internal voltage reference and returns [VrefInt], which can be used in - /// [Adc::read_internal()] to perform conversion. - pub fn enable_vrefint(&self) -> VrefInt { - T::common_regs().ccr().modify(|reg| { - reg.set_tsvrefe(true); - }); - - VrefInt {} - } - - /// Enables internal temperature sensor and returns [Temperature], which can be used in - /// [Adc::read_internal()] to perform conversion. - /// - /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, - /// temperature sensor will return vbat value. - pub fn enable_temperature(&self) -> Temperature { - T::common_regs().ccr().modify(|reg| { - reg.set_tsvrefe(true); - }); + if let Some(resolution) = config.resolution { + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + } - Temperature {} + Self { adc } } - /// Enables vbat input and returns [Vbat], which can be used in - /// [Adc::read_internal()] to perform conversion. - pub fn enable_vbat(&self) -> Vbat { - T::common_regs().ccr().modify(|reg| { - reg.set_vbate(true); - }); - - Vbat {} - } + pub(super) fn enable() {} pub(super) fn start() { // Begin ADC conversions @@ -192,18 +128,31 @@ where } pub(super) fn stop() { + let r = T::regs(); + // Stop ADC - T::regs().cr2().modify(|reg| { + r.cr2().modify(|reg| { // Stop ADC reg.set_swstart(false); + // Stop ADC + reg.set_adon(false); + // Stop DMA + reg.set_dma(false); }); - } - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + 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); } - pub(super) fn blocking_convert() -> u16 { + pub(super) fn convert() -> u16 { // clear end of conversion flag T::regs().sr().modify(|reg| { reg.set_eoc(false); @@ -224,7 +173,44 @@ where T::regs().dr().read().0 as u16 } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + pub(super) fn configure_dma(conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Repeated(_) => { + 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); + }); + } + } + } + + pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { T::regs().cr2().modify(|reg| { reg.set_adon(true); }); @@ -234,7 +220,7 @@ where r.set_l((sequence.len() - 1).try_into().unwrap()); }); - for (i, (ch, sample_time)) in sequence.enumerate() { + for (i, ((ch, _), sample_time)) in sequence.enumerate() { // Set the channel in the right sequence field. T::regs().sqr3().modify(|w| w.set_sq(i, ch)); @@ -247,62 +233,37 @@ where } } - pub(super) fn setup_dma() { - 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); + /// Enables internal voltage reference and returns [VrefInt], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(true); }); - 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); - }); + VrefInt {} } - pub(super) fn teardown_dma() { - 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); + /// Enables internal temperature sensor and returns [Temperature], which can be used in + /// [Adc::read_internal()] to perform conversion. + /// + /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, + /// temperature sensor will return vbat value. + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(true); }); - r.cr1().modify(|w| { - // Disable interrupt for end of conversion - w.set_eocie(false); - // Disable interrupt for overrun - w.set_ovrie(false); - }); + Temperature {} + } - clear_interrupt_flags(r); + /// Enables vbat input and returns [Vbat], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vbat(&self) -> Vbat { + T::common_regs().ccr().modify(|reg| { + reg.set_vbate(true); + }); - compiler_fence(Ordering::SeqCst); + Vbat {} } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index cbc217545..62b5043ee 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,9 +1,9 @@ use cfg_if::cfg_if; #[cfg(adc_g0)] use heapless::Vec; -use pac::adc::vals::Dmacfg; #[cfg(adc_g0)] -use pac::adc::vals::{Ckmode, Smpsel}; +use pac::adc::vals::Ckmode; +use pac::adc::vals::Dmacfg; #[cfg(adc_v3)] use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; #[cfg(adc_g0)] @@ -11,18 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; -use super::{ - Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, - blocking_delay_us, -}; - -#[cfg(any(adc_v3, adc_g0, adc_u0))] -mod ringbuffered; - -#[cfg(any(adc_v3, adc_g0, adc_u0))] -use ringbuffered::RingBufferedAdc; - -use crate::dma::Transfer; +use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use crate::adc::ConversionMode; use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. @@ -89,7 +79,7 @@ impl super::SealedSpecialConverter for T { cfg_if! { if #[cfg(any(adc_h5, adc_h7rs))] { pub struct VddCore; - impl AdcChannel for VddCore {} + impl super::AdcChannel for VddCore {} impl super::SealedAdcChannel for VddCore { fn channel(&self) -> u8 { 6 @@ -101,7 +91,7 @@ cfg_if! { cfg_if! { if #[cfg(adc_u0)] { pub struct DacOut; - impl AdcChannel for DacOut {} + impl super::AdcChannel for DacOut {} impl super::SealedAdcChannel for DacOut { fn channel(&self) -> u8 { 19 @@ -145,6 +135,32 @@ pub enum Clock { }} +#[cfg(adc_u0)] +type Ovss = u8; +#[cfg(adc_u0)] +type Ovsr = u8; +#[cfg(adc_v3)] +type Ovss = OversamplingShift; +#[cfg(adc_v3)] +type Ovsr = OversamplingRatio; + +/// Adc configuration +#[derive(Default)] +pub struct AdcConfig { + #[cfg(any(adc_u0, adc_g0, adc_v3))] + pub oversampling_shift: Option, + #[cfg(any(adc_u0, adc_g0, adc_v3))] + pub oversampling_ratio: Option, + #[cfg(any(adc_u0, adc_g0))] + pub oversampling_enable: Option, + #[cfg(adc_v3)] + pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, + #[cfg(adc_g0)] + pub clock: Option, + pub resolution: Option, + pub averaging: Option, +} + impl<'d, T: Instance> Adc<'d, T> { /// Enable the voltage regulator fn init_regulator() { @@ -178,38 +194,6 @@ 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_dma() { - //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 { @@ -218,6 +202,73 @@ impl<'d, T: Instance> Adc<'d, T> { Self { adc } } + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + #[cfg(not(adc_g0))] + let s = Self::new(adc); + + #[cfg(adc_g0)] + let s = match config.clock { + Some(clock) => Self::new_with_clock(adc, clock), + None => Self::new(adc), + }; + + #[cfg(any(adc_g0, adc_u0, adc_v3))] + if let Some(shift) = config.oversampling_shift { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + + #[cfg(any(adc_g0, adc_u0, adc_v3))] + if let Some(ratio) = config.oversampling_ratio { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(any(adc_g0, adc_u0))] + if let Some(enable) = config.oversampling_enable { + T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); + } + + #[cfg(adc_v3)] + if let Some((mode, trig_mode, enable)) = config.oversampling_mode { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + + if let Some(resolution) = config.resolution { + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } + + if let Some(averaging) = config.averaging { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, 0, 0), + Averaging::Samples2 => (true, 0, 1), + Averaging::Samples4 => (true, 1, 2), + Averaging::Samples8 => (true, 2, 3), + Averaging::Samples16 => (true, 3, 4), + Averaging::Samples32 => (true, 4, 5), + Averaging::Samples64 => (true, 5, 6), + Averaging::Samples128 => (true, 6, 7), + Averaging::Samples256 => (true, 7, 8), + }; + T::regs().cfgr2().modify(|reg| { + #[cfg(not(any(adc_g0, adc_u0)))] + reg.set_rovse(enable); + #[cfg(any(adc_g0, adc_u0))] + reg.set_ovse(enable); + #[cfg(any(adc_h5, adc_h7rs))] + reg.set_ovsr(samples.into()); + #[cfg(not(any(adc_h5, adc_h7rs)))] + reg.set_ovsr(samples.into()); + reg.set_ovss(right_shift.into()); + }) + } + + s + } + #[cfg(adc_g0)] /// Initialize ADC with explicit clock for the analog ADC pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { @@ -255,7 +306,7 @@ impl<'d, T: Instance> Adc<'d, T> { } // Enable ADC only when it is not already running. - fn enable(&mut self) { + pub(super) fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -276,258 +327,75 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn enable_vrefint(&self) -> VrefInt { - #[cfg(not(any(adc_g0, adc_u0)))] - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - - // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us - // to stabilize the internal voltage reference. - blocking_delay_us(15); - - VrefInt {} - } - - pub fn enable_temperature(&self) -> Temperature { - cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_tsen(true); - }); - } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_tsen(true); - }); - } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch17sel(true); - }); - } + pub(super) fn start() { + #[cfg(any(adc_v3, adc_g0, adc_u0))] + { + // Start adc conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); } - - Temperature {} } - pub fn enable_vbat(&self) -> Vbat { - cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_vbaten(true); - }); - } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); - }); - } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch18sel(true); + pub(super) fn stop() { + #[cfg(any(adc_v3, adc_g0, adc_u0))] + { + // Ensure conversions are finished. + 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() {} } - } - - Vbat {} - } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); - } - pub fn set_averaging(&mut self, averaging: Averaging) { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, 0, 0), - Averaging::Samples2 => (true, 0, 1), - Averaging::Samples4 => (true, 1, 2), - Averaging::Samples8 => (true, 2, 3), - Averaging::Samples16 => (true, 3, 4), - Averaging::Samples32 => (true, 4, 5), - Averaging::Samples64 => (true, 5, 6), - Averaging::Samples128 => (true, 6, 7), - Averaging::Samples256 => (true, 7, 8), - }; - T::regs().cfgr2().modify(|reg| { + // Reset configuration. #[cfg(not(any(adc_g0, adc_u0)))] - reg.set_rovse(enable); + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(false); + }); #[cfg(any(adc_g0, adc_u0))] - reg.set_ovse(enable); - #[cfg(any(adc_h5, adc_h7rs))] - reg.set_ovsr(samples.into()); - #[cfg(not(any(adc_h5, adc_h7rs)))] - reg.set_ovsr(samples.into()); - reg.set_ovss(right_shift.into()); - }) - } - /* - /// Convert a raw sample from the `Temperature` to deg C - pub fn to_degrees_centigrade(sample: u16) -> f32 { - (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) - * (sample as f32 - VtempCal30::get().read() as f32) - + 30.0 - } - */ - - /// Perform a single conversion. - fn convert(&mut self) -> u16 { - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - while !T::regs().isr().read().eos() { - // spin + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(false); + }); } - - T::regs().dr().read().0 as u16 } - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.read_channel(channel, sample_time) - } - - /// Read one or multiple ADC channels using DMA. - /// - /// `readings` must have a length that is a multiple of the length of the - /// `sequence` iterator. - /// - /// Note: The order of values in `readings` is defined by the pin ADC - /// channel number and not the pin order in `sequence`. - /// - /// Example - /// ```rust,ignore - /// 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 mut measurements = [0u16; 2]; - /// - /// adc.read( - /// p.DMA1_CH2.reborrow(), - /// [ - /// (&mut *adc_pin0, SampleTime::CYCLES160_5), - /// (&mut *adc_pin1, SampleTime::CYCLES160_5), - /// ] - /// .into_iter(), - /// &mut measurements, - /// ) - /// .await; - /// defmt::info!("measurements: {}", measurements); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, - readings: &mut [u16], - ) { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - readings.len() % sequence.len() == 0, - "Readings length must be a multiple of sequence length" - ); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); - - // Ensure no conversions are ongoing and ADC is enabled. - Self::cancel_conversions(); - self.enable(); + /// Perform a single conversion. + pub(super) fn convert() -> u16 { + // Some models are affected by an erratum: + // If we perform conversions slower than 1 kHz, the first read ADC value can be + // corrupted, so we discard it and measure again. + // + // STM32L471xx: Section 2.7.3 + // STM32G4: Section 2.7.3 + #[cfg(any(rcc_l4, rcc_g4))] + let len = 2; - // Set sequence length - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().sqr1().modify(|w| { - w.set_l(sequence.len() as u8 - 1); - }); + #[cfg(not(any(rcc_l4, rcc_g4)))] + let len = 1; - #[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 - ); - } - } - } - }) + for _ in 0..len { + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); }); - } - #[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); - // 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!(), - } + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); - #[cfg(adc_u0)] - { - channel_mask |= 1 << channel.channel(); - } + while !T::regs().isr().read().eos() { + // spin } - - // 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); - }); } + + T::regs().dr().read().0 as u16 + } + + pub(super) fn configure_dma(conversion_mode: ConversionMode) { // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { @@ -535,82 +403,23 @@ impl<'d, T: Instance> Adc<'d, T> { }); #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); - reg.set_dmaen(true); - }); + let regs = T::regs().cfgr(); + #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { + let regs = T::regs().cfgr1(); + + regs.modify(|reg| { reg.set_discen(false); reg.set_cont(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); + reg.set_dmacfg(match conversion_mode { + ConversionMode::Singular => Dmacfg::ONE_SHOT, + ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, + }); reg.set_dmaen(true); }); - - 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. - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_cont(false); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_cont(false); - }); } - /// 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 - #[cfg(any(adc_v3, adc_g0, adc_u0))] - 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 - + pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().modify(|w| { @@ -623,10 +432,10 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().chselr().write(|chselr| { T::regs().smpr().write(|smpr| { - for (channel, sample_time) in sequence { - chselr.set_chsel(channel.channel.into(), true); + for ((channel, _), sample_time) in sequence { + chselr.set_chsel(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()); + smpr.set_smpsel(channel.into(), (i as u8).into()); } else { smpr.set_sample_time(sample_times.len(), sample_time); if let Err(_) = sample_times.push(sample_time) { @@ -646,30 +455,63 @@ impl<'d, T: Instance> Adc<'d, T> { let mut channel_mask = 0; // Configure channels and ranks - for (_i, (mut channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(&mut channel, sample_time); + for (_i, ((channel, _), sample_time)) in sequence.enumerate() { + // RM0492, RM0481, etc. + // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." + #[cfg(any(adc_h5, adc_h7rs))] + if channel == 0 { + T::regs().or().modify(|reg| reg.set_op0(true)); + } + + // Configure channel + cfg_if! { + if #[cfg(adc_u0)] { + // On G0 and U6 all channels use the same sampling time. + T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + match channel { + 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), + _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), + } + } else { + let sample_time = sample_time.into(); + T::regs() + .smpr(channel as usize / 10) + .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); + } + } + + #[cfg(stm32h7)] + { + use crate::pac::adc::vals::Pcsel; + + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); + } // 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()); + w.set_sq(_i, channel); }); } 4..=8 => { T::regs().sqr2().modify(|w| { - w.set_sq(_i - 4, channel.channel()); + w.set_sq(_i - 4, channel); }); } 9..=13 => { T::regs().sqr3().modify(|w| { - w.set_sq(_i - 9, channel.channel()); + w.set_sq(_i - 9, channel); }); } 14..=15 => { T::regs().sqr4().modify(|w| { - w.set_sq(_i - 14, channel.channel()); + w.set_sq(_i - 14, channel); }); } _ => unreachable!(), @@ -677,7 +519,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(adc_u0)] { - channel_mask |= 1 << channel.channel(); + channel_mask |= 1 << channel; } } @@ -689,151 +531,71 @@ impl<'d, T: Instance> Adc<'d, T> { 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); - }); + } + pub fn enable_vrefint(&self) -> VrefInt { #[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); + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(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); + T::regs().ccr().modify(|reg| { + reg.set_vrefen(true); }); - RingBufferedAdc::new(dma, dma_buf) - } - - #[cfg(not(adc_g0))] - 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." - #[cfg(any(adc_h5, adc_h7rs))] - if channel.channel() == 0 { - T::regs().or().modify(|reg| reg.set_op0(true)); - } + // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us + // to stabilize the internal voltage reference. + blocking_delay_us(15); - // Configure channel - Self::set_channel_sample_time(channel.channel(), sample_time); + VrefInt {} } - fn read_channel(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.enable(); - #[cfg(not(adc_g0))] - Self::configure_channel(channel, sample_time); - #[cfg(adc_g0)] - T::regs().smpr().write(|reg| { - reg.set_sample_time(0, sample_time); - reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); - }); - // Select channel - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); - #[cfg(any(adc_g0, adc_u0))] - T::regs().chselr().write(|reg| { - #[cfg(adc_g0)] - reg.set_chsel(channel.channel().into(), true); - #[cfg(adc_u0)] - reg.set_chsel(1 << channel.channel()); - }); - - // Some models are affected by an erratum: - // If we perform conversions slower than 1 kHz, the first read ADC value can be - // corrupted, so we discard it and measure again. - // - // STM32L471xx: Section 2.7.3 - // STM32G4: Section 2.7.3 - #[cfg(any(rcc_l4, rcc_g4))] - let _ = self.convert(); - let val = self.convert(); - - T::regs().cr().modify(|reg| reg.set_addis(true)); - - // RM0492, RM0481, etc. - // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." - #[cfg(any(adc_h5, adc_h7rs))] - if channel.channel() == 0 { - T::regs().or().modify(|reg| reg.set_op0(false)); + pub fn enable_temperature(&self) -> Temperature { + cfg_if! { + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_tsen(true); + }); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + T::common_regs().ccr().modify(|reg| { + reg.set_tsen(true); + }); + } else { + T::common_regs().ccr().modify(|reg| { + reg.set_ch17sel(true); + }); + } } - val - } - - #[cfg(adc_g0)] - pub fn set_oversampling_shift(&mut self, shift: Ovss) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - #[cfg(adc_u0)] - pub fn set_oversampling_shift(&mut self, shift: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - - #[cfg(adc_g0)] - pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - #[cfg(adc_u0)] - pub fn set_oversampling_ratio(&mut self, ratio: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - #[cfg(any(adc_g0, adc_u0))] - pub fn oversampling_enable(&mut self, enable: bool) { - T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); - } - - #[cfg(adc_v3)] - pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { - T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - } - - #[cfg(adc_v3)] - pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - #[cfg(adc_v3)] - pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + Temperature {} } - #[cfg(not(adc_g0))] - fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + pub fn enable_vbat(&self) -> Vbat { cfg_if! { - if #[cfg(adc_u0)] { - // On G0 and U6 all channels use the same sampling time. - T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); } else if #[cfg(any(adc_h5, adc_h7rs))] { - match _ch { - 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), - _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), - } + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); } else { - let sample_time = sample_time.into(); - T::regs() - .smpr(_ch as usize / 10) - .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); + T::common_regs().ccr().modify(|reg| { + reg.set_ch18sel(true); + }); } } + + Vbat {} } - fn cancel_conversions() { - 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() {} - } + /* + /// Convert a raw sample from the `Temperature` to deg C + pub fn to_degrees_centigrade(sample: u16) -> f32 { + (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) + * (sample as f32 - VtempCal30::get().read() as f32) + + 30.0 } + */ } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1d5d3fb92..9be6bcd0b 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -4,11 +4,8 @@ use pac::adc::vals::{Adcaldif, Boost}; use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{ - Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, - VrefInt, blocking_delay_us, -}; -use crate::dma::Transfer; +use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use crate::adc::ConversionMode; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -147,7 +144,48 @@ pub enum Averaging { Samples1024, } +/// Adc configuration +#[derive(Default)] +pub struct AdcConfig { + pub resolution: Option, + pub averaging: Option, +} + impl<'d, T: Instance> Adc<'d, T> { + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + let s = Self::new(adc); + + // Set the ADC resolution. + if let Some(resolution) = config.resolution { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + // Set hardware averaging. + if let Some(averaging) = config.averaging { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, 0, 0), + Averaging::Samples2 => (true, 1, 1), + Averaging::Samples4 => (true, 3, 2), + Averaging::Samples8 => (true, 7, 3), + Averaging::Samples16 => (true, 15, 4), + Averaging::Samples32 => (true, 31, 5), + Averaging::Samples64 => (true, 63, 6), + Averaging::Samples128 => (true, 127, 7), + Averaging::Samples256 => (true, 255, 8), + Averaging::Samples512 => (true, 511, 9), + Averaging::Samples1024 => (true, 1023, 10), + }; + + T::regs().cfgr2().modify(|reg| { + reg.set_rovse(enable); + reg.set_ovsr(samples); + reg.set_ovss(right_shift); + }) + } + + s + } + /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); @@ -179,37 +217,20 @@ impl<'d, T: Instance> Adc<'d, T> { }; T::regs().cr().modify(|w| w.set_boost(boost)); } - let mut s = Self { adc }; - s.power_up(); - s.configure_differential_inputs(); - - s.calibrate(); - blocking_delay_us(1); - - s.enable(); - s.configure(); - - s - } - fn power_up(&mut self) { T::regs().cr().modify(|reg| { reg.set_deeppwd(false); reg.set_advregen(true); }); blocking_delay_us(10); - } - fn configure_differential_inputs(&mut self) { T::regs().difsel().modify(|w| { for n in 0..20 { w.set_difsel(n, Difsel::SINGLE_ENDED); } }); - } - fn calibrate(&mut self) { T::regs().cr().modify(|w| { #[cfg(not(adc_u5))] w.set_adcaldif(Adcaldif::SINGLE_ENDED); @@ -219,80 +240,50 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|w| w.set_adcal(true)); while T::regs().cr().read().adcal() {} - } - fn enable(&mut self) { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } + blocking_delay_us(1); + + Self::enable(); - fn configure(&mut self) { // single conversion mode, software trigger T::regs().cfgr().modify(|w| { w.set_cont(false); w.set_exten(Exten::DISABLED); }); - } - /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> VrefInt { - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - - VrefInt {} + Self { adc } } - /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> Temperature { - T::common_regs().ccr().modify(|reg| { - reg.set_vsenseen(true); - }); - - Temperature {} + pub(super) fn enable() { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); } - /// Enable reading the vbat internal channel. - pub fn enable_vbat(&self) -> Vbat { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); + pub(super) fn start() { + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); }); - - Vbat {} } - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - } + pub(super) 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() {} + } - /// Set hardware averaging. - pub fn set_averaging(&mut self, averaging: Averaging) { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, 0, 0), - Averaging::Samples2 => (true, 1, 1), - Averaging::Samples4 => (true, 3, 2), - Averaging::Samples8 => (true, 7, 3), - Averaging::Samples16 => (true, 15, 4), - Averaging::Samples32 => (true, 31, 5), - Averaging::Samples64 => (true, 63, 6), - Averaging::Samples128 => (true, 127, 7), - Averaging::Samples256 => (true, 255, 8), - Averaging::Samples512 => (true, 511, 9), - Averaging::Samples1024 => (true, 1023, 10), - }; - - T::regs().cfgr2().modify(|reg| { - reg.set_rovse(enable); - reg.set_ovsr(samples); - reg.set_ovss(right_shift); - }) + // Reset configuration. + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmngt(Dmngt::from_bits(0)); + }); } - /// Perform a single conversion. - fn convert(&mut self) -> u16 { + pub(super) fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -310,170 +301,96 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.read_channel(channel, sample_time) + pub(super) fn configure_dma(conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Singular => { + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + }); + T::regs().cfgr().modify(|reg| { + reg.set_cont(true); + reg.set_dmngt(Dmngt::DMA_ONE_SHOT); + }); + } + _ => unreachable!(), + } } - /// Read one or multiple ADC channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::{Adc, AdcChannel} - /// - /// let mut adc = Adc::new(p.ADC1); - /// let mut adc_pin0 = p.PA0.into(); - /// let mut adc_pin2 = p.PA2.into(); - /// let mut measurements = [0u16; 2]; - /// - /// adc.read( - /// p.DMA2_CH0.reborrow(), - /// [ - /// (&mut *adc_pin0, SampleTime::CYCLES112), - /// (&mut *adc_pin2, SampleTime::CYCLES112), - /// ] - /// .into_iter(), - /// &mut measurements, - /// ) - /// .await; - /// defmt::info!("measurements: {}", measurements); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, - readings: &mut [u16], - ) { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - - // Ensure no conversions are ongoing - Self::cancel_conversions(); - + pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); }); // Configure channels and ranks - for (i, (channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(channel, sample_time); + for (i, ((channel, _), sample_time)) in sequence.enumerate() { + let sample_time = sample_time.into(); + if channel <= 9 { + T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); + } else { + T::regs() + .smpr(1) + .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); + } + + #[cfg(any(stm32h7, stm32u5))] + { + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + } + match i { 0..=3 => { T::regs().sqr1().modify(|w| { - w.set_sq(i, channel.channel()); + w.set_sq(i, channel); }); } 4..=8 => { T::regs().sqr2().modify(|w| { - w.set_sq(i - 4, channel.channel()); + w.set_sq(i - 4, channel); }); } 9..=13 => { T::regs().sqr3().modify(|w| { - w.set_sq(i - 9, channel.channel()); + w.set_sq(i - 9, channel); }); } 14..=15 => { T::regs().sqr4().modify(|w| { - w.set_sq(i - 14, channel.channel()); + w.set_sq(i - 14, channel); }); } _ => unreachable!(), } } - - // Set continuous mode with oneshot dma. - // Clear overrun flag before starting transfer. - - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - }); - T::regs().cfgr().modify(|reg| { - reg.set_cont(true); - reg.set_dmngt(Dmngt::DMA_ONE_SHOT); - }); - - 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().cfgr().modify(|reg| { - reg.set_cont(false); - reg.set_dmngt(Dmngt::from_bits(0)); - }); } - fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { - channel.setup(); - - let channel = channel.channel(); - - Self::set_channel_sample_time(channel, sample_time); + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); - #[cfg(any(stm32h7, stm32u5))] - { - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() - .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); - } + VrefInt {} } - fn read_channel(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - Self::configure_channel(channel, sample_time); - - T::regs().sqr1().modify(|reg| { - reg.set_sq(0, channel.channel()); - reg.set_l(0); + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); }); - self.convert() + Temperature {} } - fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { - let sample_time = sample_time.into(); - if ch <= 9 { - T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); - } else { - T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); - } - } + /// Enable reading the vbat internal channel. + pub fn enable_vbat(&self) -> Vbat { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); - 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() {} - } + Vbat {} } } diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5628cb827..694e85657 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut delay = Delay; - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new_with_config(p.ADC1, Default::default()); let mut pin = p.PC1; let mut vrefint = adc.enable_vrefint(); diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 01b881c79..d61b1b2eb 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -4,7 +4,7 @@ use cortex_m::singleton; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Peripherals; -use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime}; +use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, RingBufferedAdc, SampleTime}; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; @@ -20,8 +20,8 @@ async fn adc_task(p: Peripherals) { let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); - let adc = Adc::new(p.ADC1); - let adc2 = Adc::new(p.ADC2); + let adc = Adc::new_with_config(p.ADC1, Default::default()); + let adc2 = Adc::new_with_config(p.ADC2, Default::default()); let mut adc: RingBufferedAdc = adc.into_ring_buffered( p.DMA2_CH0, @@ -31,6 +31,7 @@ async fn adc_task(p: Peripherals) { (p.PA2.degrade_adc(), SampleTime::CYCLES112), ] .into_iter(), + RegularConversionMode::Continuous, ); let mut adc2: RingBufferedAdc = adc2.into_ring_buffered( p.DMA2_CH2, @@ -40,6 +41,7 @@ async fn adc_task(p: Peripherals) { (p.PA3.degrade_adc(), SampleTime::CYCLES112), ] .into_iter(), + RegularConversionMode::Continuous, ); // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index f6979889d..aa8b1771b 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, Presc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -16,12 +16,14 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Adc oversample test"); - let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); - let mut pin = p.PA1; + let mut config = AdcConfig::default(); + config.clock = Some(Clock::Async { div: Presc::DIV1 }); + config.oversampling_ratio = Some(Ovsr::MUL16); + config.oversampling_shift = Some(Ovss::NO_SHIFT); + config.oversampling_enable = Some(true); - adc.set_oversampling_ratio(Ovsr::MUL16); - adc.set_oversampling_shift(Ovss::NO_SHIFT); - adc.oversampling_enable(true); + let mut adc = Adc::new_with_config(p.ADC1, config); + let mut pin = p.PA1; loop { let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 94315141c..2149e0748 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -28,9 +28,9 @@ async fn main(_spawner: Spawner) { let mut p = embassy_stm32::init(config); info!("Hello World!"); - let mut adc = Adc::new(p.ADC2); + let mut adc = Adc::new(p.ADC2, Default::default()); - let mut adc_temp = Adc::new(p.ADC1); + let mut adc_temp = Adc::new(p.ADC1, Default::default()); let mut temperature = adc_temp.enable_temperature(); loop { diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index 2773723e9..6dedf88d6 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new(p.ADC1, Default::default()); let mut differential_channel = (p.PA0, p.PA1); // can also use diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs index ef8b0c3c2..478b6b2ca 100644 --- a/examples/stm32g4/src/bin/adc_dma.rs +++ b/examples/stm32g4/src/bin/adc_dma.rs @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new(p.ADC1, Default::default()); let mut dma = p.DMA1_CH1; let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index 3ae2ff064..1e97fa925 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs @@ -77,7 +77,7 @@ async fn main(_spawner: embassy_executor::Spawner) { pwm.set_mms2(Mms2::UPDATE); // Configure regular conversions with DMA - let adc1 = Adc::new(p.ADC1); + let adc1 = Adc::new(p.ADC1, Default::default()); let vrefint_channel = adc1.enable_vrefint().degrade_adc(); let pa0 = p.PC1.degrade_adc(); diff --git a/examples/stm32g4/src/bin/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs index cb99ab2a7..87ffea4be 100644 --- a/examples/stm32g4/src/bin/adc_oversampling.rs +++ b/examples/stm32g4/src/bin/adc_oversampling.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; use embassy_stm32::adc::vals::{Rovsm, Trovs}; -use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -32,7 +32,8 @@ async fn main(_spawner: Spawner) { } let mut p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let mut config = AdcConfig::default(); + // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf // page652 Oversampler // Table 172. Maximum output results vs N and M. Grayed values indicates truncation @@ -44,9 +45,11 @@ async fn main(_spawner: Spawner) { // 0x05 oversampling ratio X64 // 0x06 oversampling ratio X128 // 0x07 oversampling ratio X256 - adc.set_oversampling_ratio(0x03); // ratio X3 - adc.set_oversampling_shift(0b0000); // no shift - adc.enable_regular_oversampling_mode(Rovsm::RESUMED, Trovs::AUTOMATIC, true); + config.oversampling_ratio = Some(0x03); // ratio X3 + config.oversampling_shift = Some(0b0000); // no shift + config.oversampling_mode = Some((Rovsm::RESUMED, Trovs::AUTOMATIC, true)); + + let mut adc = Adc::new(p.ADC1, config); loop { let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 835bf5411..42766a5e3 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_stm32::Config; -use embassy_stm32::adc::{Adc, Resolution, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -17,9 +17,12 @@ fn main() -> ! { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let mut config = AdcConfig::default(); + config.resolution = Some(Resolution::BITS8); + + let mut adc = Adc::new_with_config(p.ADC1, config); //adc.enable_vref(); - adc.set_resolution(Resolution::BITS8); + let mut channel = p.PC0; loop { diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index ab1e9d2e9..550da95a4 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; -use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; +use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, SampleTime}; use {defmt_rtt as _, panic_probe as _}; const DMA_BUF_LEN: usize = 512; @@ -20,7 +20,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let adc = Adc::new(p.ADC1); let adc_pin0 = p.PA0.degrade_adc(); let adc_pin1 = p.PA1.degrade_adc(); let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; @@ -29,6 +29,7 @@ async fn main(_spawner: Spawner) { p.DMA1_CH1, &mut adc_dma_buf, [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), + RegularConversionMode::Continuous, ); info!("starting measurement loop"); diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 4fbc6f17f..53bd37303 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_stm32::Config; -use embassy_stm32::adc::{Adc, Resolution, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; @@ -18,8 +18,9 @@ fn main() -> ! { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); - adc.set_resolution(Resolution::BITS8); + let mut config = AdcConfig::default(); + config.resolution = Some(Resolution::BITS8); + let mut adc = Adc::new_with_config(p.ADC1, config); let mut channel = p.PC0; loop { diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 99944f7c7..6b9a91d6e 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{self, AdcChannel, SampleTime, adc4}; +use embassy_stm32::adc::{self, AdcChannel, AdcConfig, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,19 +12,21 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut p = embassy_stm32::init(config); // **** ADC1 init **** - let mut adc1 = adc::Adc::new(p.ADC1); + let mut config = AdcConfig::default(); + config.averaging = Some(adc::Averaging::Samples1024); + config.resolution = Some(adc::Resolution::BITS14); + let mut adc1 = adc::Adc::new_with_config(p.ADC1, config); let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 let mut adc1_pin2 = p.PA2; // A1 - adc1.set_resolution(adc::Resolution::BITS14); - adc1.set_averaging(adc::Averaging::Samples1024); let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); // **** ADC2 init **** - let mut adc2 = adc::Adc::new(p.ADC2); + let mut config = AdcConfig::default(); + config.averaging = Some(adc::Averaging::Samples1024); + config.resolution = Some(adc::Resolution::BITS14); + let mut adc2 = adc::Adc::new_with_config(p.ADC2, config); let mut adc2_pin1 = p.PC3; // A2 let mut adc2_pin2 = p.PB0; // A3 - adc2.set_resolution(adc::Resolution::BITS14); - adc2.set_averaging(adc::Averaging::Samples1024); let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); // **** ADC4 init **** -- cgit