mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-27 12:20:37 +00:00
Merge pull request #4586 from per42/adc_with_clock
Add Adc::new_with_clock() to configure analog clock
This commit is contained in:
commit
9c30be254c
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
39
examples/stm32wl/src/bin/adc.rs
Normal file
39
examples/stm32wl/src/bin/adc.rs
Normal 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user