From 53f65d8b09c15fcedefd41d721f53ebd296229de Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 18 May 2022 18:34:36 +0200 Subject: [PATCH] Automatically set ADC clock prescaler on v2 ADC to respect max frequency --- embassy-stm32/src/adc/v2.rs | 46 +++++++++++++++++++++++++++++++-- examples/stm32f7/src/bin/adc.rs | 26 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 examples/stm32f7/src/bin/adc.rs diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index d2429b111..ab71c0f52 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,4 +1,5 @@ use crate::adc::{AdcPin, Instance}; +use crate::time::Hertz; use core::marker::PhantomData; use embassy::util::Unborrow; use embassy_hal_common::unborrow; @@ -6,12 +7,12 @@ use embedded_hal_02::blocking::delay::DelayUs; pub const VDDA_CALIB_MV: u32 = 3000; -#[cfg(not(rcc_f4))] +#[cfg(not(any(rcc_f4, rcc_f7)))] fn enable() { todo!() } -#[cfg(rcc_f4)] +#[cfg(any(rcc_f4, rcc_f7))] fn enable() { critical_section::with(|_| unsafe { // TODO do not enable all adc clocks if not needed @@ -114,6 +115,39 @@ impl Default for SampleTime { } } +enum Prescaler { + Div2, + Div4, + Div6, + Div8, +} + +impl Prescaler { + fn from_pclk2(freq: Hertz) -> Self { + // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. + const MAX_FREQUENCY: Hertz = Hertz(36_000_000); + let raw_div = freq.0 / MAX_FREQUENCY.0; + match raw_div { + 0..=1 => Self::Div2, + 2..=3 => Self::Div4, + 4..=5 => Self::Div6, + 6..=7 => Self::Div8, + _ => panic!( + "Selected PCLK2 frequency is too high for ADC with largest possible prescaler." + ), + } + } + + fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { + match self { + Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, + Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, + Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, + Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, + } + } +} + pub struct Adc<'d, T: Instance> { sample_time: SampleTime, calibrated_vdda: u32, @@ -128,6 +162,14 @@ where pub fn new(_peri: impl Unborrow + 'd, delay: &mut impl DelayUs) -> Self { unborrow!(_peri); enable(); + + let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) }; + unsafe { + T::common_regs() + .ccr() + .modify(|w| w.set_adcpre(presc.adcpre())); + } + unsafe { // disable before config is set T::regs().cr2().modify(|reg| { diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs new file mode 100644 index 000000000..87f5d30dd --- /dev/null +++ b/examples/stm32f7/src/bin/adc.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use defmt::*; +use embassy::executor::Spawner; +use embassy::time::{Delay, Duration, Timer}; +use embassy_stm32::adc::Adc; +use embassy_stm32::Peripherals; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut pin = p.PA3; + + loop { + let v = adc.read(&mut pin); + info!("--> {} - {} mV", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; + } +}