From 046778fc53fb996445948e0cb98aaa4253fe8201 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 27 Jul 2022 01:17:26 +0300 Subject: [PATCH] Improve ADC configuration options --- embassy-stm32/src/adc/v2.rs | 26 +++++++++++++------- embassy-stm32/src/adc/v3.rs | 42 ++++++++++++++++++++------------- embassy-stm32/src/adc/v4.rs | 28 ++++++++++++++++------ examples/stm32h7/src/bin/adc.rs | 6 ++--- 4 files changed, 68 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 5c608451b..a74c00979 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -7,7 +7,10 @@ use crate::adc::{AdcPin, Instance}; use crate::time::Hertz; use crate::Peripheral; -pub const VDDA_CALIB_MV: u32 = 3000; +/// 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 = 3300; #[cfg(not(any(rcc_f4, rcc_f7)))] fn enable() { @@ -47,7 +50,7 @@ impl Resolution { } } - fn to_max_count(&self) -> u32 { + pub fn to_max_count(&self) -> u32 { match self { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, @@ -57,9 +60,9 @@ impl Resolution { } } -pub struct Vref; -impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +pub struct VrefInt; +impl AdcPin for VrefInt {} +impl super::sealed::AdcPin for VrefInt { fn channel(&self) -> u8 { 17 } @@ -150,7 +153,7 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - calibrated_vdda: u32, + vref: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -180,7 +183,7 @@ where Self { sample_time: Default::default(), resolution: Resolution::default(), - calibrated_vdda: VDDA_CALIB_MV, + vref: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -193,9 +196,16 @@ where self.resolution = resolution; } + /// Set VREF, which is used for [to_millivolts()] conversion. + /// + /// Use this if you have a known precise VREF (VDDA) pin reference voltage. + pub fn set_vref(&mut self, vref: u32) { + self.vref = vref; + } + /// Convert a measurement to millivolts pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.calibrated_vdda) / self.resolution.to_max_count()) as u16 + ((u32::from(sample) * self.vref) / self.resolution.to_max_count()) as u16 } /// Perform a single conversion. diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index dbfd18810..32e115fa1 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -6,7 +6,10 @@ use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{AdcPin, Instance}; use crate::Peripheral; -pub const VDDA_CALIB_MV: u32 = 3000; +/// 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; /// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent ADC clock /// configuration. @@ -44,7 +47,7 @@ impl Resolution { } } - fn to_max_count(&self) -> u32 { + pub fn to_max_count(&self) -> u32 { match self { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, @@ -54,9 +57,9 @@ impl Resolution { } } -pub struct Vref; -impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +pub struct VrefInt; +impl AdcPin for VrefInt {} +impl super::sealed::AdcPin for VrefInt { fn channel(&self) -> u8 { #[cfg(not(stm32g0))] let val = 0; @@ -202,7 +205,7 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - calibrated_vdda: u32, + vref: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -241,12 +244,12 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), resolution: Resolution::default(), - calibrated_vdda: VDDA_CALIB_MV, + vref: VREF_DEFAULT_MV, phantom: PhantomData, } } - pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref { + pub fn enable_vrefint(&self, delay: &mut impl DelayUs) -> VrefInt { unsafe { T::common_regs().ccr().modify(|reg| { reg.set_vrefen(true); @@ -259,7 +262,7 @@ impl<'d, T: Instance> Adc<'d, T> { //cortex_m::asm::delay(20_000_000); delay.delay_us(15); - Vref {} + VrefInt {} } pub fn enable_temperature(&self) -> Temperature { @@ -282,16 +285,16 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat {} } - /// Calculates the system VDDA by sampling the internal VREF channel and comparing + /// Calculates the system VDDA by sampling the internal VREFINT channel and comparing /// the result with the value stored at the factory. If the chip's VDDA is not stable, run /// this before each ADC conversion. #[cfg(not(stm32g0))] // TODO is this supposed to be public? #[allow(unused)] // TODO is this supposed to be public? - fn calibrate(&mut self, vref: &mut Vref) { + fn calibrate(&mut self, vrefint: &mut VrefInt) { #[cfg(stm32l5)] - let vref_cal: u32 = todo!(); + let vrefint_cal: u32 = todo!(); #[cfg(not(stm32l5))] - let vref_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() }; + let vrefint_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() }; let old_sample_time = self.sample_time; // "Table 24. Embedded internal voltage reference" states that the sample time needs to be @@ -300,11 +303,11 @@ impl<'d, T: Instance> Adc<'d, T> { self.sample_time = SampleTime::Cycles640_5; // This can't actually fail, it's just in a result to satisfy hal trait - let vref_samp = self.read(vref); + let vrefint_samp = self.read(vrefint); self.sample_time = old_sample_time; - self.calibrated_vdda = (VDDA_CALIB_MV * u32::from(vref_cal)) / u32::from(vref_samp); + self.vref = (VREF_CALIB_MV * u32::from(vrefint_cal)) / u32::from(vrefint_samp); } pub fn set_sample_time(&mut self, sample_time: SampleTime) { @@ -315,9 +318,16 @@ impl<'d, T: Instance> Adc<'d, T> { self.resolution = resolution; } + /// Set VREF used for [to_millivolts()] conversion. + /// + /// Use this if you have a known precise VREF (VDDA) pin reference voltage. + pub fn set_vref(&mut self, vref: u32) { + self.vref = vref; + } + /// Convert a measurement to millivolts pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.calibrated_vdda) / self.resolution.to_max_count()) as u16 + ((u32::from(sample) * self.vref) / self.resolution.to_max_count()) as u16 } /* diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 92e8ac369..0950b4661 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -9,6 +9,11 @@ use super::{AdcPin, Instance}; use crate::time::Hertz; use crate::{pac, Peripheral}; +/// 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 = 3300; + pub enum Resolution { SixteenBit, FourteenBit, @@ -53,10 +58,10 @@ mod sealed { } } -// NOTE: Vref/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs -pub struct Vref; -impl InternalChannel for Vref {} -impl sealed::InternalChannel for Vref { +// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs +pub struct VrefInt; +impl InternalChannel for VrefInt {} +impl sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { 19 } @@ -317,6 +322,7 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, + vref: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -354,6 +360,7 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { let mut s = Self { sample_time: Default::default(), + vref: VREF_DEFAULT_MV, resolution: Resolution::default(), phantom: PhantomData, }; @@ -422,14 +429,14 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } } - pub fn enable_vref(&self) -> Vref { + pub fn enable_vrefint(&self) -> VrefInt { unsafe { T::common_regs().ccr().modify(|reg| { reg.set_vrefen(true); }); } - Vref {} + VrefInt {} } pub fn enable_temperature(&self) -> Temperature { @@ -460,9 +467,16 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { self.resolution = resolution; } + /// Set VREF used for [to_millivolts()] conversion. + /// + /// Use this if you have a known precise VREF (VDDA) pin reference voltage. + pub fn set_vref(&mut self, vref: u32) { + self.vref = vref; + } + /// Convert a measurement to millivolts pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * 3300) / self.resolution.to_max_count()) as u16 + ((u32::from(sample) * self.vref) / self.resolution.to_max_count()) as u16 } /// Perform a single conversion. diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index ce73364c0..d8a5d23d7 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -28,11 +28,11 @@ async fn main(_spawner: Spawner, mut p: Peripherals) { adc.set_sample_time(SampleTime::Cycles32_5); - let mut vref_channel = adc.enable_vref(); + let mut vrefint_channel = adc.enable_vrefint(); loop { - let vref = adc.read_internal(&mut vref_channel); - info!("vref: {}", vref); + let vrefint = adc.read_internal(&mut vrefint_channel); + info!("vrefint: {}", vrefint); let measured = adc.read(&mut p.PC0); info!("measured: {}", measured); Timer::after(Duration::from_millis(500)).await;