Merge pull request #4586 from per42/adc_with_clock

Add Adc::new_with_clock() to configure analog clock
This commit is contained in:
Dario Nieuwenhuis 2025-09-05 15:29:34 +02:00 committed by GitHub
commit 9c30be254c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 132 additions and 7 deletions

View File

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- feat: stm32/adc/v3: allow DMA reads to loop through enable channels
- fix: Fix XSPI not disabling alternate bytes when they were previously enabled
- fix: Fix stm32h7rs init when using external flash via XSPI
- feat: Add Adc::new_with_clock() to configure analog clock
- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923))
## 0.3.0 - 2025-08-12

View File

@ -107,8 +107,50 @@ pub enum Averaging {
Samples128,
Samples256,
}
cfg_if! { if #[cfg(adc_g0)] {
/// Synchronous PCLK prescaler
/// * ADC_CFGR2:CKMODE in STM32WL5x
#[repr(u8)]
pub enum CkModePclk {
DIV1 = 3,
DIV2 = 1,
DIV4 = 2,
}
/// Asynchronous ADCCLK prescaler
/// * ADC_CCR:PRESC in STM32WL5x
#[repr(u8)]
pub enum Presc {
DIV1,
DIV2,
DIV4,
DIV6,
DIV8,
DIV10,
DIV12,
DIV16,
DIV32,
DIV64,
DIV128,
DIV256,
}
/// 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 },
}
}}
impl<'d, T: Instance> Adc<'d, T> {
pub fn new(adc: Peri<'d, T>) -> Self {
/// Enable the voltage regulator
fn init_regulator() {
rcc::enable_and_reset::<T>();
T::regs().cr().modify(|reg| {
#[cfg(not(any(adc_g0, adc_u0)))]
@ -117,13 +159,17 @@ impl<'d, T: Instance> Adc<'d, T> {
});
// 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);
});
@ -133,6 +179,45 @@ impl<'d, T: Instance> Adc<'d, T> {
}
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,
sample_time: SampleTime::from_bits(0),
}
}
#[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 as u8)),
Clock::Sync { div } => T::regs().cfgr2().modify(|reg| reg.set_ckmode(div as u8)),
}
Self::init_calibrate();
Self {
adc,

View File

@ -3,7 +3,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, SampleTime};
use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let mut adc = Adc::new(p.ADC1);
let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });
adc.set_sample_time(SampleTime::CYCLES79_5);
let mut pin = p.PA1;

View File

@ -3,7 +3,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime};
use embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut adc = Adc::new(p.ADC1);
let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });
let mut dma = p.DMA1_CH1;
let mut vrefint_channel = adc.enable_vrefint().degrade_adc();

View File

@ -7,7 +7,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, SampleTime};
use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Adc oversample test");
let mut adc = Adc::new(p.ADC1);
let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });
adc.set_sample_time(SampleTime::CYCLES1_5);
let mut pin = p.PA1;

View File

@ -0,0 +1,39 @@
#![no_std]
#![no_main]
use core::mem::MaybeUninit;
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime};
use embassy_stm32::SharedData;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);
info!("Hello World!");
let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 });
adc.set_sample_time(SampleTime::CYCLES79_5);
let mut pin = p.PB2;
let mut vrefint = adc.enable_vrefint();
let vrefint_sample = adc.blocking_read(&mut vrefint);
let convert_to_millivolts = |sample| {
// From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf
// 6.3.3 Embedded internal reference voltage
const VREFINT_MV: u32 = 1212; // mV
(u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16
};
loop {
let v = adc.blocking_read(&mut pin);
info!("--> {} - {} mV", v, convert_to_millivolts(v));
Timer::after_millis(100).await;
}
}