Automatically set ADC clock prescaler on v2 ADC to respect max frequency

This commit is contained in:
Matous Hybl 2022-05-18 18:34:36 +02:00
parent b7a27113f0
commit 53f65d8b09
2 changed files with 70 additions and 2 deletions

View file

@ -1,4 +1,5 @@
use crate::adc::{AdcPin, Instance}; use crate::adc::{AdcPin, Instance};
use crate::time::Hertz;
use core::marker::PhantomData; use core::marker::PhantomData;
use embassy::util::Unborrow; use embassy::util::Unborrow;
use embassy_hal_common::unborrow; use embassy_hal_common::unborrow;
@ -6,12 +7,12 @@ use embedded_hal_02::blocking::delay::DelayUs;
pub const VDDA_CALIB_MV: u32 = 3000; pub const VDDA_CALIB_MV: u32 = 3000;
#[cfg(not(rcc_f4))] #[cfg(not(any(rcc_f4, rcc_f7)))]
fn enable() { fn enable() {
todo!() todo!()
} }
#[cfg(rcc_f4)] #[cfg(any(rcc_f4, rcc_f7))]
fn enable() { fn enable() {
critical_section::with(|_| unsafe { critical_section::with(|_| unsafe {
// TODO do not enable all adc clocks if not needed // 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> { pub struct Adc<'d, T: Instance> {
sample_time: SampleTime, sample_time: SampleTime,
calibrated_vdda: u32, calibrated_vdda: u32,
@ -128,6 +162,14 @@ where
pub fn new(_peri: impl Unborrow<Target = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { pub fn new(_peri: impl Unborrow<Target = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
unborrow!(_peri); unborrow!(_peri);
enable(); 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 { unsafe {
// disable before config is set // disable before config is set
T::regs().cr2().modify(|reg| { T::regs().cr2().modify(|reg| {

View file

@ -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;
}
}