From 92f2c6d09c3bd2d11d8a5c7a56bd4208f9720895 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Thu, 30 Dec 2021 10:51:49 +0100 Subject: [PATCH] adc: Implement support for f1 ADC block Add basic support for the STM32F1xx ADC blocks. --- embassy-stm32/src/adc/f1.rs | 233 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/adc/mod.rs | 9 ++ 2 files changed, 242 insertions(+) create mode 100644 embassy-stm32/src/adc/f1.rs diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs new file mode 100644 index 00000000..3ed1701f --- /dev/null +++ b/embassy-stm32/src/adc/f1.rs @@ -0,0 +1,233 @@ +use crate::adc::{AdcPin, Instance}; +use crate::rcc::get_freqs; +use crate::time::Hertz; +use core::marker::PhantomData; +use embassy::util::Unborrow; +use embassy_hal_common::unborrow; +use embedded_hal::blocking::delay::DelayUs; + +pub const VDDA_CALIB_MV: u32 = 3300; +pub const ADC_MAX: u32 = (1 << 12) - 1; +// No calibration data for F103, voltage should be 1.2v +pub const VREF_INT: u32 = 1200; + +pub struct Vref; +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 17 + } +} + +pub struct Temperature; +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +mod sample_time { + /// ADC sample time + /// + /// The default setting is 1.5 ADC clock cycles. + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + /// 1.5 ADC clock cycles + Cycles1_5 = 0b000, + + /// 7.5 ADC clock cycles + Cycles7_5 = 0b001, + + /// 13.5 ADC clock cycles + Cycles13_5 = 0b010, + + /// 28.5 ADC clock cycles + Cycles28_5 = 0b011, + + /// 41.5 ADC clock cycles + Cycles41_5 = 0b100, + + /// 55.5 ADC clock cycles + Cycles55_5 = 0b101, + + /// 71.5 ADC clock cycles + Cycles71_5 = 0b110, + + /// 239.5 ADC clock cycles + Cycles239_5 = 0b111, + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { + match self { + SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5, + SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5, + SampleTime::Cycles13_5 => crate::pac::adc::vals::SampleTime::CYCLES13_5, + SampleTime::Cycles28_5 => crate::pac::adc::vals::SampleTime::CYCLES28_5, + SampleTime::Cycles41_5 => crate::pac::adc::vals::SampleTime::CYCLES41_5, + SampleTime::Cycles55_5 => crate::pac::adc::vals::SampleTime::CYCLES55_5, + SampleTime::Cycles71_5 => crate::pac::adc::vals::SampleTime::CYCLES71_5, + SampleTime::Cycles239_5 => crate::pac::adc::vals::SampleTime::CYCLES239_5, + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::Cycles28_5 + } + } +} + +pub use sample_time::SampleTime; + +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + calibrated_vdda: u32, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new(_peri: impl Unborrow + 'd, delay: &mut impl DelayUs) -> Self { + unborrow!(_peri); + T::enable(); + T::reset(); + unsafe { + T::regs().cr2().modify(|reg| reg.set_adon(true)); + } + + // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) + // for at least two ADC clock cycles + delay.delay_us((1_000_000 * 2) / Self::freq().0 + 1); + + unsafe { + // Reset calibration + T::regs().cr2().modify(|reg| reg.set_rstcal(true)); + while T::regs().cr2().read().rstcal() { + // spin + } + + // Calibrate + T::regs().cr2().modify(|reg| reg.set_cal(true)); + while T::regs().cr2().read().cal() { + // spin + } + } + + // One cycle after calibration + delay.delay_us((1_000_000) / Self::freq().0 + 1); + + Self { + sample_time: Default::default(), + calibrated_vdda: VDDA_CALIB_MV, + phantom: PhantomData, + } + } + + fn freq() -> Hertz { + unsafe { get_freqs() }.adc + } + + pub fn sample_time_for_us(&self, us: u32) -> SampleTime { + match us * Self::freq().0 / 1_000_000 { + 0..=1 => SampleTime::Cycles1_5, + 2..=7 => SampleTime::Cycles7_5, + 8..=13 => SampleTime::Cycles13_5, + 14..=28 => SampleTime::Cycles28_5, + 29..=41 => SampleTime::Cycles41_5, + 42..=55 => SampleTime::Cycles55_5, + 56..=71 => SampleTime::Cycles71_5, + _ => SampleTime::Cycles239_5, + } + } + + pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref { + unsafe { + T::regs().cr2().modify(|reg| { + reg.set_tsvrefe(true); + }) + } + Vref {} + } + + pub fn enable_temperature(&self) -> Temperature { + unsafe { + T::regs().cr2().modify(|reg| { + reg.set_tsvrefe(true); + }) + } + Temperature {} + } + + /// Calculates the system VDDA by sampling the internal VREF channel and comparing + /// to the expected value. If the chip's VDDA is not stable, run this before each ADC + /// conversion. + pub fn calibrate(&mut self, vref: &mut Vref) -> u32 { + let old_sample_time = self.sample_time; + self.sample_time = SampleTime::Cycles239_5; + + let vref_samp = self.read(vref); + self.sample_time = old_sample_time; + + self.calibrated_vdda = (ADC_MAX * VREF_INT) / u32::from(vref_samp); + self.calibrated_vdda + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + /// Convert a measurement to millivolts + pub fn to_millivolts(&self, sample: u16) -> u16 { + ((u32::from(sample) * self.calibrated_vdda) / ADC_MAX) as u16 + } + + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + unsafe { + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + reg.set_swstart(true); + }); + while T::regs().cr2().read().swstart() {} + while !T::regs().sr().read().eoc() {} + + T::regs().dr().read().0 as u16 + } + } + + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + unsafe { + Self::set_channel_sample_time(pin.channel(), self.sample_time); + T::regs().cr1().modify(|reg| { + reg.set_scan(false); + reg.set_discen(false); + }); + T::regs().sqr1().modify(|reg| reg.set_l(0)); + + T::regs().cr2().modify(|reg| { + reg.set_cont(false); + reg.set_exttrig(true); + reg.set_swstart(false); + reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART); + }); + } + + // Configure the channel to sample + unsafe { T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())) } + self.convert() + } + + unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + if ch <= 9 { + T::regs() + .smpr2() + .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + } else { + T::regs() + .smpr1() + .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + } + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 9c3a30fb..d0fc188d 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -3,6 +3,7 @@ #[cfg_attr(adc_v3, path = "v3.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(adc_g0, path = "v3.rs")] +#[cfg_attr(adc_f1, path = "f1.rs")] mod _version; #[allow(unused)] @@ -13,9 +14,11 @@ use crate::peripherals; pub(crate) mod sealed { pub trait Instance { fn regs() -> &'static crate::pac::adc::Adc; + #[cfg(not(adc_f1))] fn common_regs() -> &'static crate::pac::adccommon::AdcCommon; } + #[cfg(not(adc_f1))] pub trait Common { fn regs() -> &'static crate::pac::adccommon::AdcCommon; } @@ -25,7 +28,11 @@ pub(crate) mod sealed { } } +#[cfg(not(adc_f1))] pub trait Instance: sealed::Instance + 'static {} +#[cfg(adc_f1)] +pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} +#[cfg(not(adc_f1))] pub trait Common: sealed::Common + 'static {} pub trait AdcPin: sealed::AdcPin {} @@ -35,6 +42,7 @@ crate::pac::peripherals!( fn regs() -> &'static crate::pac::adc::Adc { &crate::pac::$inst } + #[cfg(not(adc_f1))] fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { crate::pac::peripherals!{ (adccommon, $common_inst:ident) => { @@ -48,6 +56,7 @@ crate::pac::peripherals!( }; ); +#[cfg(not(adc_f1))] crate::pac::peripherals!( (adccommon, $inst:ident) => { impl sealed::Common for peripherals::$inst {