From f0506252e21c96ce3b03e0d1c061a831d7dfe3c3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Nov 2025 20:06:00 -0600 Subject: stm32: extract adc4 extract adc4 into common adc system and add anyInstance trait to cover adc4 and not adc4 --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/build.rs | 4 +- embassy-stm32/src/adc/adc4.rs | 312 ++++++++++++++-------------------- embassy-stm32/src/adc/g4.rs | 206 +++++++++++----------- embassy-stm32/src/adc/injected.rs | 6 +- embassy-stm32/src/adc/mod.rs | 111 +++++++----- embassy-stm32/src/adc/ringbuffered.rs | 12 +- embassy-stm32/src/adc/v2.rs | 66 +++---- embassy-stm32/src/adc/v3.rs | 302 ++++++++++++++++---------------- embassy-stm32/src/adc/v4.rs | 220 ++++++++++++------------ examples/stm32u5/src/bin/adc.rs | 21 ++- examples/stm32wba/src/bin/adc.rs | 17 +- examples/stm32wba6/src/bin/adc.rs | 17 +- 13 files changed, 651 insertions(+), 644 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ec49924a2..2f4f2ce51 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -188,6 +188,7 @@ embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } chrono = { version = "^0.4", default-features = false, optional = true } bit_field = "0.10.2" +trait-set = "0.3.0" document-features = "0.2.7" static_assertions = { version = "1.1" } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 48da475df..ad6743f96 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1602,13 +1602,13 @@ fn main() { .into(); if chip_name.starts_with("stm32u5") { - signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); + signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } else { signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } if chip_name.starts_with("stm32wba") { - signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); + signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } if chip_name.starts_with("stm32g4") { diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 04d976513..52678d1b6 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -4,8 +4,8 @@ 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, blocking_delay_us}; -use crate::dma::Transfer; +use super::blocking_delay_us; +use crate::adc::ConversionMode; #[cfg(stm32u5)] pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; #[cfg(stm32wba)] @@ -153,6 +153,121 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri type Interrupt: crate::interrupt::typelevel::Interrupt; } +foreach_adc!( + (ADC4, $common_inst:ident, $clock:ident) => { + use crate::peripherals::ADC4; + + impl super::BasicAnyInstance for ADC4 { + type SampleTime = SampleTime; + } + + impl super::SealedAnyInstance for ADC4 { + fn dr() -> *mut u16 { + ADC4::regs().dr().as_ptr() as *mut u16 + } + + fn enable() { + ADC4::regs().isr().write(|w| w.set_adrdy(true)); + ADC4::regs().cr().modify(|w| w.set_aden(true)); + while !ADC4::regs().isr().read().adrdy() {} + ADC4::regs().isr().write(|w| w.set_adrdy(true)); + } + + fn start() { + // Start conversion + ADC4::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + } + + fn stop() { + if ADC4::regs().cr().read().adstart() && !ADC4::regs().cr().read().addis() { + ADC4::regs().cr().modify(|reg| { + reg.set_adstp(true); + }); + while ADC4::regs().cr().read().adstart() {} + } + + // Reset configuration. + ADC4::regs().cfgr1().modify(|reg| { + reg.set_dmaen(false); + }); + } + + fn configure_dma(conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Singular => { + ADC4::regs().isr().modify(|reg| { + reg.set_ovr(true); + reg.set_eos(true); + reg.set_eoc(true); + }); + + ADC4::regs().cfgr1().modify(|reg| { + reg.set_dmaen(true); + reg.set_dmacfg(Dmacfg::ONE_SHOT); + #[cfg(stm32u5)] + reg.set_chselrmod(false); + #[cfg(stm32wba)] + reg.set_chselrmod(Chselrmod::ENABLE_INPUT) + }); + } + _ => unreachable!(), + } + } + + fn configure_sequence(sequence: impl ExactSizeIterator) { + let mut prev_channel: i16 = -1; + #[cfg(stm32wba)] + ADC4::regs().chselr().write_value(Chselr(0_u32)); + #[cfg(stm32u5)] + ADC4::regs().chselrmod0().write_value(Chselr(0_u32)); + for (_i, ((channel, _), sample_time)) in sequence.enumerate() { + ADC4::regs().smpr().modify(|w| { + w.set_smp(_i, sample_time); + }); + + let channel_num = channel; + if channel_num as i16 <= prev_channel { + return; + }; + prev_channel = channel_num as i16; + + #[cfg(stm32wba)] + ADC4::regs().chselr().modify(|w| { + w.set_chsel0(channel as usize, true); + }); + #[cfg(stm32u5)] + ADC4::regs().chselrmod0().modify(|w| { + w.set_chsel(channel as usize, true); + }); + } + } + + fn convert() -> u16 { + // Reset interrupts + ADC4::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + ADC4::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !ADC4::regs().isr().read().eos() { + // spin + } + + ADC4::regs().dr().read().0 as u16 + } + } + + impl super::AnyInstance for ADC4 {} + }; +); + pub struct Adc4<'d, T: Instance> { #[allow(unused)] adc: crate::Peri<'d, T>, @@ -164,9 +279,9 @@ pub enum Adc4Error { DMAError, } -impl<'d, T: Instance> Adc4<'d, T> { +impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { + pub fn new_adc4(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); let prescaler = Prescaler::from_ker_ck(T::frequency()); @@ -200,7 +315,7 @@ impl<'d, T: Instance> Adc4<'d, T> { blocking_delay_us(1); - Self::enable(); + T::enable(); // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { @@ -231,15 +346,8 @@ impl<'d, T: Instance> Adc4<'d, T> { 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) -> super::VrefInt { + pub fn enable_vrefint_adc4(&self) -> super::VrefInt { T::regs().ccr().modify(|w| { w.set_vrefen(true); }); @@ -248,7 +356,7 @@ impl<'d, T: Instance> Adc4<'d, T> { } /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> super::Temperature { + pub fn enable_temperature_adc4(&self) -> super::Temperature { T::regs().ccr().modify(|w| { w.set_vsensesel(true); }); @@ -258,7 +366,7 @@ impl<'d, T: Instance> Adc4<'d, T> { /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_vbat(&self) -> super::Vbat { + pub fn enable_vbat_adc4(&self) -> super::Vbat { T::regs().ccr().modify(|w| { w.set_vbaten(true); }); @@ -267,13 +375,13 @@ impl<'d, T: Instance> Adc4<'d, T> { } /// Enable reading the vbat internal channel. - pub fn enable_vcore(&self) -> super::Vcore { + pub fn enable_vcore_adc4(&self) -> super::Vcore { super::Vcore {} } /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_dac_channel(&self, dac: DacChannel) -> super::Dac { + pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { let mux; match dac { DacChannel::OUT1 => mux = false, @@ -284,13 +392,13 @@ impl<'d, T: Instance> Adc4<'d, T> { } /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { + pub fn set_resolution_adc4(&mut self, resolution: Resolution) { T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); } /// Set hardware averaging. #[cfg(stm32u5)] - pub fn set_averaging(&mut self, averaging: Averaging) { + pub fn set_averaging_adc4(&mut self, averaging: Averaging) { let (enable, samples, right_shift) = match averaging { Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), @@ -310,7 +418,7 @@ impl<'d, T: Instance> Adc4<'d, T> { }) } #[cfg(stm32wba)] - pub fn set_averaging(&mut self, averaging: Averaging) { + pub fn set_averaging_adc4(&mut self, averaging: Averaging) { let (enable, samples, right_shift) = match averaging { Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), @@ -329,168 +437,4 @@ impl<'d, T: Instance> Adc4<'d, T> { w.set_ovse(enable) }) } - - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - T::regs().smpr().modify(|w| { - w.set_smp(0, sample_time); - }); - - channel.setup(); - - // Select channel - #[cfg(stm32wba)] - { - T::regs().chselr().write_value(Chselr(0_u32)); - T::regs().chselr().modify(|w| { - w.set_chsel0(channel.channel() as usize, true); - }); - } - #[cfg(stm32u5)] - { - T::regs().chselrmod0().write_value(Chselr(0_u32)); - T::regs().chselrmod0().modify(|w| { - w.set_chsel(channel.channel() as usize, true); - }); - } - - // Reset interrupts - 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().dr().read().0 as u16 - } - - /// Read one or multiple ADC channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// The channels in `sequence` must be in ascending order. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::adc4; - /// use embassy_stm32::adc::AdcChannel; - /// - /// let mut adc4 = adc4::Adc4::new(p.ADC4); - /// let mut adc4_pin1 = p.PC1; - /// let mut adc4_pin2 = p.PC0; - /// let mut.into()d41 = adc4_pin1.into(); - /// let mut.into()d42 = adc4_pin2.into(); - /// let mut measurements = [0u16; 2]; - /// // not that the channels must be in ascending order - /// adc4.read( - /// &mut p.GPDMA1_CH1, - /// [ - /// &mut.into()d42, - /// &mut.into()d41, - /// ] - /// .into_iter(), - /// &mut measurements, - /// ).await.unwrap(); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma4>, - sequence: impl ExactSizeIterator>, - readings: &mut [u16], - ) -> Result<(), Adc4Error> { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - - // Ensure no conversions are ongoing - Self::cancel_conversions(); - - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - reg.set_eos(true); - reg.set_eoc(true); - }); - - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); - #[cfg(stm32u5)] - reg.set_chselrmod(false); - #[cfg(stm32wba)] - reg.set_chselrmod(Chselrmod::ENABLE_INPUT) - }); - - // Verify and activate sequence - let mut prev_channel: i16 = -1; - #[cfg(stm32wba)] - T::regs().chselr().write_value(Chselr(0_u32)); - #[cfg(stm32u5)] - T::regs().chselrmod0().write_value(Chselr(0_u32)); - for channel in sequence { - let channel_num = channel.channel; - if channel_num as i16 <= prev_channel { - return Err(Adc4Error::InvalidSequence); - }; - prev_channel = channel_num as i16; - - #[cfg(stm32wba)] - T::regs().chselr().modify(|w| { - w.set_chsel0(channel.channel as usize, true); - }); - #[cfg(stm32u5)] - T::regs().chselrmod0().modify(|w| { - w.set_chsel(channel.channel as usize, 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); - }); - - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(false); - }); - - if T::regs().isr().read().ovr() { - Err(Adc4Error::DMAError) - } else { - Ok(()) - } - } - - 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() {} - } - } } diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 0a9f35a5b..71dc8acc0 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -12,7 +12,7 @@ use super::{ Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, blocking_delay_us, }; -use crate::adc::SealedAdcChannel; +use crate::adc::{AnyInstance, SealedAdcChannel}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -122,98 +122,12 @@ pub struct ConversionTrigger { pub edge: Exten, } -impl<'d, T: Instance> Adc<'d, T> { - /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { - rcc::enable_and_reset::(); - - let prescaler = Prescaler::from_ker_ck(T::frequency()); - - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); - - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); - trace!("ADC frequency set to {}", frequency); - - if frequency > MAX_ADC_CLK_FREQ { - panic!( - "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", - MAX_ADC_CLK_FREQ.0 / 1_000_000 - ); - } - - T::regs().cr().modify(|reg| { - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - - blocking_delay_us(20); - - T::regs().difsel().modify(|w| { - for n in 0..18 { - w.set_difsel(n, Difsel::SINGLE_ENDED); - } - }); - - T::regs().cr().modify(|w| { - w.set_adcaldif(Adcaldif::SINGLE_ENDED); - }); - - T::regs().cr().modify(|w| w.set_adcal(true)); - - while T::regs().cr().read().adcal() {} - - blocking_delay_us(20); - - T::regs().cr().modify(|w| { - w.set_adcaldif(Adcaldif::DIFFERENTIAL); - }); - - T::regs().cr().modify(|w| w.set_adcal(true)); - - while T::regs().cr().read().adcal() {} - - blocking_delay_us(20); - - Self::enable(); - - // single conversion mode, software trigger - T::regs().cfgr().modify(|w| { - w.set_cont(false); - 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 } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - /// Enable the ADC - pub(super) fn enable() { + fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -234,15 +148,13 @@ impl<'d, T: Instance> Adc<'d, T> { } } - /// Start regular adc conversion - pub(super) fn start() { + fn start() { T::regs().cr().modify(|reg| { reg.set_adstart(true); }); } - /// Stop regular conversions and disable DMA - pub(super) fn stop() { + fn stop() { if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { T::regs().cr().modify(|reg| { reg.set_adstp(Adstp::STOP); @@ -259,8 +171,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } - /// Perform a single conversion. - pub(super) fn convert() -> u16 { + fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -278,7 +189,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { T::regs().isr().modify(|reg| { reg.set_ovr(true); }); @@ -316,7 +227,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); @@ -374,6 +285,97 @@ impl<'d, T: Instance> Adc<'d, T> { } } } +} + +impl<'d, T: Instance + AnyInstance> Adc<'d, T> { + /// Create a new ADC driver. + pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { + rcc::enable_and_reset::(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + trace!("ADC frequency set to {}", frequency); + + if frequency > MAX_ADC_CLK_FREQ { + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); + } + + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + blocking_delay_us(20); + + T::regs().difsel().modify(|w| { + for n in 0..18 { + w.set_difsel(n, Difsel::SINGLE_ENDED); + } + }); + + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::SINGLE_ENDED); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + blocking_delay_us(20); + + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::DIFFERENTIAL); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + blocking_delay_us(20); + + T::enable(); + + // single conversion mode, software trigger + T::regs().cfgr().modify(|w| { + w.set_cont(false); + 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 } + } /// Enable reading the voltage reference internal channel. pub fn enable_vrefint(&self) -> super::VrefInt @@ -464,8 +466,8 @@ impl<'d, T: Instance> Adc<'d, T> { NR_INJECTED_RANKS ); - Self::stop(); - Self::enable(); + T::stop(); + T::enable(); T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); @@ -536,7 +538,7 @@ impl<'d, T: Instance> Adc<'d, T> { self, dma: Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - regular_sequence: impl ExactSizeIterator, SampleTime)>, + regular_sequence: impl ExactSizeIterator, T::SampleTime)>, regular_conversion_mode: RegularConversionMode, injected_sequence: [(AnyAdcChannel, SampleTime); N], injected_trigger: ConversionTrigger, diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index 7bb3a541c..ccaa5d1b2 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs @@ -5,9 +5,9 @@ use core::sync::atomic::{Ordering, compiler_fence}; use embassy_hal_internal::Peri; use super::{AnyAdcChannel, SampleTime}; -use crate::adc::Adc; #[allow(unused_imports)] use crate::adc::Instance; +use crate::adc::{Adc, AnyInstance}; /// Injected ADC sequence with owned channels. pub struct InjectedAdc { @@ -36,9 +36,9 @@ impl InjectedAdc { } } -impl Drop for InjectedAdc { +impl Drop for InjectedAdc { fn drop(&mut self) { - Adc::::stop(); + T::stop(); compiler_fence(Ordering::SeqCst); } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index bf404d6ef..856c2e61e 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -41,15 +41,10 @@ pub use crate::pac::adc::vals::Res as Resolution; pub use crate::pac::adc::vals::SampleTime; use crate::peripherals; -#[cfg(not(adc_wba))] -dma_trait!(RxDma, Instance); -#[cfg(adc_u5)] -dma_trait!(RxDma4, adc4::Instance); -#[cfg(adc_wba)] -dma_trait!(RxDma4, adc4::Instance); +dma_trait!(RxDma, AnyInstance); /// Analog to Digital driver. -pub struct Adc<'d, T: Instance> { +pub struct Adc<'d, T: AnyInstance> { #[allow(unused)] adc: crate::Peri<'d, T>, } @@ -92,6 +87,49 @@ 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)))] +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(); + fn stop(); + fn convert() -> u16; + fn configure_dma(conversion_mode: ConversionMode); + fn configure_sequence(sequence: impl ExactSizeIterator); + 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))] +#[allow(private_bounds)] +pub trait AnyInstance: SealedAnyInstance + Instance {} + +// On chips with ADC4, AnyInstance is an Instance or adc4::Instance +#[cfg(any(adc_v4, adc_u5, adc_wba))] +#[allow(private_bounds)] +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))] +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))] +impl AnyInstance for T {} + /// Performs a busy-wait delay for a specified number of microseconds. #[allow(unused)] pub(crate) fn blocking_delay_us(us: u32) { @@ -110,16 +148,18 @@ 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))] +#[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 { + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] 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))] +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] // Conversion mode for regular ADC channels +#[allow(dead_code)] #[derive(Copy, Clone)] pub enum RegularConversionMode { // Samples as fast as possible @@ -129,21 +169,21 @@ pub enum RegularConversionMode { 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))] +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))] /// Read an ADC pin. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { + 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))] channel.setup(); #[cfg(not(adc_v4))] - Self::enable(); - Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + T::enable(); + T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); - Self::convert() + T::convert() } - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] /// Read one or multiple ADC regular channels using DMA. /// /// `sequence` iterator and `readings` must have the same length. @@ -175,7 +215,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub async fn read( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, + sequence: impl ExactSizeIterator, T::SampleTime)>, readings: &mut [u16], ) { assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); @@ -189,33 +229,26 @@ impl<'d, T: Instance> Adc<'d, T> { ); // Ensure no conversions are ongoing and ADC is enabled. - Self::stop(); - Self::enable(); + T::stop(); + T::enable(); - Self::configure_sequence( + T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); - Self::configure_dma(ConversionMode::Singular); + T::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(), - ) - }; + let transfer = + unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; - Self::start(); + T::start(); // Wait for conversion sequence to finish. transfer.await; // Ensure conversions are finished. - Self::stop(); + T::stop(); } #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] @@ -244,7 +277,7 @@ impl<'d, T: Instance> Adc<'d, T> { self, dma: embassy_hal_internal::Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, SampleTime)>, + sequence: impl ExactSizeIterator, T::SampleTime)>, mode: RegularConversionMode, ) -> RingBufferedAdc<'a, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); @@ -254,15 +287,15 @@ impl<'d, T: Instance> Adc<'d, T> { "Asynchronous read sequence cannot be more than 16 in length" ); // reset conversions and enable the adc - Self::stop(); - Self::enable(); + T::stop(); + T::enable(); //adc side setup - Self::configure_sequence( + T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); - Self::configure_dma(ConversionMode::Repeated(mode)); + T::configure_dma(ConversionMode::Repeated(mode)); core::mem::forget(self); diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 62ea0d3a2..a56f8ca0b 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; #[allow(unused_imports)] use embassy_hal_internal::Peri; -use crate::adc::Adc; +use crate::adc::AnyInstance; #[allow(unused_imports)] use crate::adc::{Instance, RxDma}; #[allow(unused_imports)] @@ -19,7 +19,7 @@ pub struct RingBufferedAdc<'d, T: Instance> { ring_buf: ReadableRingBuffer<'d, u16>, } -impl<'d, T: Instance> RingBufferedAdc<'d, T> { +impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { pub(crate) fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { //dma side setup let opts = TransferOptions { @@ -45,11 +45,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { compiler_fence(Ordering::SeqCst); self.ring_buf.start(); - Adc::::start(); + T::start(); } pub fn stop(&mut self) { - Adc::::stop(); + T::stop(); self.ring_buf.request_pause(); @@ -170,9 +170,9 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } } -impl Drop for RingBufferedAdc<'_, T> { +impl Drop for RingBufferedAdc<'_, T> { fn drop(&mut self) { - Adc::::stop(); + T::stop(); compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 2f9fabafb..4065f89a7 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -91,35 +91,14 @@ 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()); - T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); - T::regs().cr2().modify(|reg| { - reg.set_adon(true); - }); - - blocking_delay_us(3); - - if let Some(resolution) = config.resolution { - T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); - } - - Self { adc } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - pub(super) fn enable() {} + fn enable() {} - pub(super) fn start() { + fn start() { // Begin ADC conversions T::regs().cr2().modify(|reg| { reg.set_adon(true); @@ -127,7 +106,7 @@ where }); } - pub(super) fn stop() { + fn stop() { let r = T::regs(); // Stop ADC @@ -152,7 +131,7 @@ where compiler_fence(Ordering::SeqCst); } - pub(super) fn convert() -> u16 { + fn convert() -> u16 { // clear end of conversion flag T::regs().sr().modify(|reg| { reg.set_eoc(false); @@ -173,7 +152,7 @@ where T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { match conversion_mode { ConversionMode::Repeated(_) => { let r = T::regs(); @@ -210,7 +189,7 @@ where } } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { T::regs().cr2().modify(|reg| { reg.set_adon(true); }); @@ -232,6 +211,33 @@ where } } } +} + +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()); + T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + }); + + blocking_delay_us(3); + + if let Some(resolution) = config.resolution { + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + } + + Self { adc } + } /// Enables internal voltage reference and returns [VrefInt], which can be used in /// [Adc::read_internal()] to perform conversion. diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 62b5043ee..4cce1dac3 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -161,152 +161,13 @@ pub struct AdcConfig { pub averaging: Option, } -impl<'d, T: Instance> Adc<'d, T> { - /// Enable the voltage regulator - fn init_regulator() { - rcc::enable_and_reset::(); - T::regs().cr().modify(|reg| { - #[cfg(not(any(adc_g0, adc_u0)))] - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - - // If this is false then each ADC_CHSELR bit enables an input channel. - // This is the reset value, so has no effect. - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(false); - }); - - blocking_delay_us(20); - } - - /// Calibrate to remove conversion offset - fn init_calibrate() { - T::regs().cr().modify(|reg| { - reg.set_adcal(true); - }); - - while T::regs().cr().read().adcal() { - // spin - } - - blocking_delay_us(1); - } - - /// 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 { - Self::init_regulator(); - Self::init_calibrate(); - 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 { - Self::init_regulator(); - - #[cfg(any(stm32wl5x))] - { - // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual - let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; - match clock { - Clock::Async { div: _ } => { - assert!(async_clock_available); - } - Clock::Sync { div: _ } => { - if async_clock_available { - warn!("Not using configured ADC clock"); - } - } - } - } - match clock { - Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), - Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { - reg.set_ckmode(match div { - CkModePclk::DIV1 => Ckmode::PCLK, - CkModePclk::DIV2 => Ckmode::PCLK_DIV2, - CkModePclk::DIV4 => Ckmode::PCLK_DIV4, - }) - }), - } - - Self::init_calibrate(); - - Self { adc } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } // Enable ADC only when it is not already running. - pub(super) fn enable() { + fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -327,7 +188,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn start() { + fn start() { #[cfg(any(adc_v3, adc_g0, adc_u0))] { // Start adc conversion @@ -337,7 +198,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn stop() { + fn stop() { #[cfg(any(adc_v3, adc_g0, adc_u0))] { // Ensure conversions are finished. @@ -363,7 +224,7 @@ impl<'d, T: Instance> Adc<'d, T> { } /// Perform a single conversion. - pub(super) fn convert() -> u16 { + 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. @@ -395,7 +256,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { @@ -419,7 +280,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().modify(|w| { @@ -532,6 +393,151 @@ impl<'d, T: Instance> Adc<'d, T> { }); } } +} + +impl<'d, T: Instance> Adc<'d, T> { + /// Enable the voltage regulator + fn init_regulator() { + rcc::enable_and_reset::(); + T::regs().cr().modify(|reg| { + #[cfg(not(any(adc_g0, adc_u0)))] + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + // If this is false then each ADC_CHSELR bit enables an input channel. + // This is the reset value, so has no effect. + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(false); + }); + + blocking_delay_us(20); + } + + /// Calibrate to remove conversion offset + fn init_calibrate() { + T::regs().cr().modify(|reg| { + reg.set_adcal(true); + }); + + while T::regs().cr().read().adcal() { + // spin + } + + blocking_delay_us(1); + } + + /// 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 { + Self::init_regulator(); + Self::init_calibrate(); + 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 { + Self::init_regulator(); + + #[cfg(any(stm32wl5x))] + { + // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual + let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; + match clock { + Clock::Async { div: _ } => { + assert!(async_clock_available); + } + Clock::Sync { div: _ } => { + if async_clock_available { + warn!("Not using configured ADC clock"); + } + } + } + } + match clock { + Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), + Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { + reg.set_ckmode(match div { + CkModePclk::DIV1 => Ckmode::PCLK, + CkModePclk::DIV2 => Ckmode::PCLK_DIV2, + CkModePclk::DIV4 => Ckmode::PCLK_DIV4, + }) + }), + } + + Self::init_calibrate(); + + Self { adc } + } pub fn enable_vrefint(&self) -> VrefInt { #[cfg(not(any(adc_g0, adc_u0)))] diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 9be6bcd0b..43eb16fd5 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -151,124 +151,26 @@ pub struct AdcConfig { 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 +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { - rcc::enable_and_reset::(); - - let prescaler = Prescaler::from_ker_ck(T::frequency()); - - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); - - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); - info!("ADC frequency set to {}", frequency); - - if frequency > MAX_ADC_CLK_FREQ { - panic!( - "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", - MAX_ADC_CLK_FREQ.0 / 1_000_000 - ); - } - - #[cfg(stm32h7)] - { - let boost = if frequency < Hertz::khz(6_250) { - Boost::LT6_25 - } else if frequency < Hertz::khz(12_500) { - Boost::LT12_5 - } else if frequency < Hertz::mhz(25) { - Boost::LT25 - } else { - Boost::LT50 - }; - T::regs().cr().modify(|w| w.set_boost(boost)); - } - - T::regs().cr().modify(|reg| { - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - - blocking_delay_us(10); - - T::regs().difsel().modify(|w| { - for n in 0..20 { - w.set_difsel(n, Difsel::SINGLE_ENDED); - } - }); - - T::regs().cr().modify(|w| { - #[cfg(not(adc_u5))] - w.set_adcaldif(Adcaldif::SINGLE_ENDED); - w.set_adcallin(true); - }); - - T::regs().cr().modify(|w| w.set_adcal(true)); - - while T::regs().cr().read().adcal() {} - - blocking_delay_us(1); - - Self::enable(); - - // single conversion mode, software trigger - T::regs().cfgr().modify(|w| { - w.set_cont(false); - w.set_exten(Exten::DISABLED); - }); - - Self { adc } - } - - pub(super) fn enable() { + 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)); } - pub(super) fn start() { + fn start() { // Start conversion T::regs().cr().modify(|reg| { reg.set_adstart(true); }); } - pub(super) fn stop() { + fn stop() { if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { T::regs().cr().modify(|reg| { reg.set_adstp(Adstp::STOP); @@ -283,7 +185,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } - pub(super) fn convert() -> u16 { + fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -301,7 +203,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { match conversion_mode { ConversionMode::Singular => { T::regs().isr().modify(|reg| { @@ -316,7 +218,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); @@ -366,6 +268,110 @@ impl<'d, T: Instance> Adc<'d, T> { } } } +} + +impl<'d, T: Instance + super::AnyInstance> 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::(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + info!("ADC frequency set to {}", frequency); + + if frequency > MAX_ADC_CLK_FREQ { + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); + } + + #[cfg(stm32h7)] + { + let boost = if frequency < Hertz::khz(6_250) { + Boost::LT6_25 + } else if frequency < Hertz::khz(12_500) { + Boost::LT12_5 + } else if frequency < Hertz::mhz(25) { + Boost::LT25 + } else { + Boost::LT50 + }; + T::regs().cr().modify(|w| w.set_boost(boost)); + } + + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + blocking_delay_us(10); + + T::regs().difsel().modify(|w| { + for n in 0..20 { + w.set_difsel(n, Difsel::SINGLE_ENDED); + } + }); + + T::regs().cr().modify(|w| { + #[cfg(not(adc_u5))] + w.set_adcaldif(Adcaldif::SINGLE_ENDED); + w.set_adcallin(true); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + blocking_delay_us(1); + + T::enable(); + + // single conversion mode, software trigger + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }); + + Self { adc } + } /// Enable reading the voltage reference internal channel. pub fn enable_vrefint(&self) -> VrefInt { diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 6b9a91d6e..ad59c0bea 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, AdcConfig, SampleTime, adc4}; +use embassy_stm32::adc::{self, Adc, AdcChannel, AdcConfig, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +15,7 @@ async fn main(_spawner: embassy_executor::Spawner) { 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 = Adc::new_with_config(p.ADC1, config); let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 let mut adc1_pin2 = p.PA2; // A1 let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); @@ -24,17 +24,17 @@ async fn main(_spawner: embassy_executor::Spawner) { 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 = Adc::new_with_config(p.ADC2, config); let mut adc2_pin1 = p.PC3; // A2 let mut adc2_pin2 = p.PB0; // A3 let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); // **** ADC4 init **** - let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4 = Adc::new_adc4(p.ADC4); let mut adc4_pin1 = p.PC1; // A4 let mut adc4_pin2 = p.PC0; // A5 - adc4.set_resolution(adc4::Resolution::BITS12); - adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_resolution_adc4(adc4::Resolution::BITS12); + adc4.set_averaging_adc4(adc4::Averaging::Samples256); let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); // **** ADC1 blocking read **** @@ -95,11 +95,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // The channels must be in ascending order and can't repeat for ADC4 adc4.read( p.GPDMA1_CH1.reborrow(), - [&mut degraded42, &mut degraded41].into_iter(), + [ + (&mut degraded42, adc4::SampleTime::CYCLES1_5), + (&mut degraded41, adc4::SampleTime::CYCLES1_5), + ] + .into_iter(), &mut measurements, ) - .await - .unwrap(); + .await; let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; let volt1: f32 = 3.3 * measurements[1] as f32 / max4 as f32; info!("Async read 4 pin 1 {}", volt1); diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs index 177aab3f3..ade3f5d6a 100644 --- a/examples/stm32wba/src/bin/adc.rs +++ b/examples/stm32wba/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{AdcChannel, adc4}; +use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,11 +12,11 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut p = embassy_stm32::init(config); // **** ADC4 init **** - let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4 = Adc::new_adc4(p.ADC4); let mut adc4_pin1 = p.PA0; // A4 let mut adc4_pin2 = p.PA1; // A5 - adc4.set_resolution(adc4::Resolution::BITS12); - adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_resolution_adc4(adc4::Resolution::BITS12); + adc4.set_averaging_adc4(adc4::Averaging::Samples256); let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); @@ -37,11 +37,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // The channels must be in ascending order and can't repeat for ADC4 adc4.read( p.GPDMA1_CH1.reborrow(), - [&mut degraded42, &mut degraded41].into_iter(), + [ + (&mut degraded42, SampleTime::CYCLES12_5), + (&mut degraded41, SampleTime::CYCLES12_5), + ] + .into_iter(), &mut measurements, ) - .await - .unwrap(); + .await; let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; info!("Async read 4 pin 1 {}", volt1); diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs index 0887e124c..9d1f39419 100644 --- a/examples/stm32wba6/src/bin/adc.rs +++ b/examples/stm32wba6/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{AdcChannel, adc4}; +use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,11 +12,11 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut p = embassy_stm32::init(config); // **** ADC4 init **** - let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4 = Adc::new_adc4(p.ADC4); let mut adc4_pin1 = p.PA0; // A4 let mut adc4_pin2 = p.PA1; // A5 - adc4.set_resolution(adc4::Resolution::BITS12); - adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_resolution_adc4(adc4::Resolution::BITS12); + adc4.set_averaging_adc4(adc4::Averaging::Samples256); let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); // **** ADC4 blocking read **** @@ -36,11 +36,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // The channels must be in ascending order and can't repeat for ADC4 adc4.read( p.GPDMA1_CH1.reborrow(), - [&mut degraded42, &mut degraded41].into_iter(), + [ + (&mut degraded42, SampleTime::CYCLES12_5), + (&mut degraded41, SampleTime::CYCLES12_5), + ] + .into_iter(), &mut measurements, ) - .await - .unwrap(); + .await; let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; info!("Async read 4 pin 1 {}", volt1); -- cgit