use cfg_if::cfg_if; #[cfg(adc_g0)] use heapless::Vec; #[cfg(adc_g0)] use pac::adc::vals::Ckmode; use pac::adc::vals::Dmacfg; #[cfg(adc_v3)] use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; #[cfg(adc_g0)] pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; use super::{Adc, Averaging, 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. pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3000; #[cfg(adc_g0)] /// The number of variants in Smpsel // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable const SAMPLE_TIMES_CAPACITY: usize = 2; #[cfg(adc_g0)] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 13; } #[cfg(any(adc_h5, adc_h7rs))] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 17; } #[cfg(adc_u0)] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 12; } #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 0; } #[cfg(adc_g0)] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 12; } #[cfg(any(adc_h5, adc_h7rs))] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 16; } #[cfg(adc_u0)] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 11; } #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 17; } #[cfg(adc_g0)] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 14; } #[cfg(any(adc_h5, adc_h7rs))] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 16; } #[cfg(adc_u0)] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 13; } #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 18; } cfg_if! { if #[cfg(any(adc_h5, adc_h7rs))] { pub struct VddCore; impl super::AdcChannel for VddCore {} impl super::SealedAdcChannel for VddCore { fn channel(&self) -> u8 { 17 } } } } cfg_if! { if #[cfg(adc_u0)] { pub struct DacOut; impl super::AdcChannel for DacOut {} impl super::SealedAdcChannel for DacOut { fn channel(&self) -> u8 { 19 } } } } cfg_if! { if #[cfg(adc_g0)] { /// Synchronous PCLK prescaler pub enum CkModePclk { DIV1, DIV2, DIV4, } /// The analog clock is either the synchronous prescaled PCLK or /// the asynchronous prescaled ADCCLK configured by the RCC mux. /// The data sheet states the maximum analog clock frequency - /// for STM32WL55CC it is 36 MHz. pub enum Clock { Sync { div: CkModePclk }, Async { div: Presc }, } }} #[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 super::AdcRegs for crate::pac::adc::Adc { fn data(&self) -> *mut u16 { crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 } // Enable ADC only when it is not already running. fn enable(&self) { // Make sure bits are off while self.cr().read().addis() { // spin } if !self.cr().read().aden() { // Enable ADC self.isr().modify(|reg| { reg.set_adrdy(true); }); self.cr().modify(|reg| { reg.set_aden(true); }); while !self.isr().read().adrdy() { // spin } } } fn start(&self) { self.cr().modify(|reg| { reg.set_adstart(true); }); } fn stop(&self) { // Ensure conversions are finished. if self.cr().read().adstart() && !self.cr().read().addis() { self.cr().modify(|reg| { reg.set_adstp(true); }); while self.cr().read().adstart() {} } // Reset configuration. #[cfg(not(any(adc_g0, adc_u0)))] self.cfgr().modify(|reg| { reg.set_cont(false); reg.set_dmaen(false); }); #[cfg(any(adc_g0, adc_u0))] self.cfgr1().modify(|reg| { reg.set_cont(false); reg.set_dmaen(false); }); } /// Perform a single conversion. fn convert(&self) { // 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; #[cfg(not(any(rcc_l4, rcc_g4)))] let len = 1; for _ in 0..len { self.isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); }); // Start conversion self.cr().modify(|reg| { reg.set_adstart(true); }); while !self.isr().read().eos() { // spin } } } fn configure_dma(&self, conversion_mode: ConversionMode) { // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. self.isr().modify(|reg| { reg.set_ovr(true); }); #[cfg(not(any(adc_g0, adc_u0)))] let regs = self.cfgr(); #[cfg(any(adc_g0, adc_u0))] let regs = self.cfgr1(); regs.modify(|reg| { reg.set_discen(false); reg.set_cont(true); reg.set_dmacfg(match conversion_mode { ConversionMode::Singular => Dmacfg::ONE_SHOT, #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, }); reg.set_dmaen(true); }); } fn configure_sequence(&self, sequence: impl ExactSizeIterator) { #[cfg(adc_h5)] self.cr().modify(|w| w.set_aden(false)); // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] self.sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); }); #[cfg(adc_g0)] { let mut sample_times = Vec::::new(); self.chselr().write(|chselr| { self.smpr().write(|smpr| { 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.into(), (i as u8).into()); } else { smpr.set_sample_time(sample_times.len(), sample_time); if let Err(_) = sample_times.push(sample_time) { panic!( "Implementation is limited to {} unique sample times among all channels.", SAMPLE_TIMES_CAPACITY ); } } } }) }); } #[cfg(not(adc_g0))] { #[cfg(adc_u0)] let mut channel_mask = 0; #[cfg(adc_h5)] let mut difsel = 0u32; // Configure channels and ranks for (_i, ((channel, _is_differential), 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 { self.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. self.smpr().modify(|reg| reg.set_smp1(sample_time.into())); } else if #[cfg(any(adc_h5, adc_h7rs))] { match channel { 0..=9 => self.smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), _ => self.smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), } } else { let sample_time = sample_time.into(); self .smpr(channel as usize / 10) .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); } } #[cfg(stm32h7)] { use crate::pac::adc::vals::Pcsel; self.cfgr2().modify(|w| w.set_lshift(0)); self.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 => { self.sqr1().modify(|w| { w.set_sq(_i, channel); }); } 4..=8 => { self.sqr2().modify(|w| { w.set_sq(_i - 4, channel); }); } 9..=13 => { self.sqr3().modify(|w| { w.set_sq(_i - 9, channel); }); } 14..=15 => { self.sqr4().modify(|w| { w.set_sq(_i - 14, channel); }); } _ => unreachable!(), } #[cfg(adc_h5)] { difsel |= (_is_differential as u32) << channel; } #[cfg(adc_u0)] { channel_mask |= 1 << channel; } } #[cfg(adc_h5)] self.difsel().write(|w| w.set_difsel(difsel)); // 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)] self.chselr().modify(|reg| { reg.set_chsel(channel_mask); }); } } } 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)))] 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); }); } } 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); }); } } Vbat {} } pub fn disable_vbat(&self) { cfg_if! { if #[cfg(any(adc_g0, adc_u0))] { T::regs().ccr().modify(|reg| { reg.set_vbaten(false); }); } else if #[cfg(any(adc_h5, adc_h7rs))] { T::common_regs().ccr().modify(|reg| { reg.set_vbaten(false); }); } else { T::common_regs().ccr().modify(|reg| { reg.set_ch18sel(false); }); } } } /* /// 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 } */ }