mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-29 05:11:31 +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
|
- 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 XSPI not disabling alternate bytes when they were previously enabled
|
||||||
- fix: Fix stm32h7rs init when using external flash via XSPI
|
- 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))
|
- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923))
|
||||||
|
|
||||||
## 0.3.0 - 2025-08-12
|
## 0.3.0 - 2025-08-12
|
||||||
|
@ -107,8 +107,50 @@ pub enum Averaging {
|
|||||||
Samples128,
|
Samples128,
|
||||||
Samples256,
|
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> {
|
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>();
|
rcc::enable_and_reset::<T>();
|
||||||
T::regs().cr().modify(|reg| {
|
T::regs().cr().modify(|reg| {
|
||||||
#[cfg(not(any(adc_g0, adc_u0)))]
|
#[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.
|
// 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))]
|
#[cfg(any(adc_g0, adc_u0))]
|
||||||
T::regs().cfgr1().modify(|reg| {
|
T::regs().cfgr1().modify(|reg| {
|
||||||
reg.set_chselrmod(false);
|
reg.set_chselrmod(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
blocking_delay_us(20);
|
blocking_delay_us(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calibrate to remove conversion offset
|
||||||
|
fn init_calibrate() {
|
||||||
T::regs().cr().modify(|reg| {
|
T::regs().cr().modify(|reg| {
|
||||||
reg.set_adcal(true);
|
reg.set_adcal(true);
|
||||||
});
|
});
|
||||||
@ -133,6 +179,45 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blocking_delay_us(1);
|
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 {
|
Self {
|
||||||
adc,
|
adc,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::adc::{Adc, SampleTime};
|
use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
info!("Hello World!");
|
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);
|
adc.set_sample_time(SampleTime::CYCLES79_5);
|
||||||
let mut pin = p.PA1;
|
let mut pin = p.PA1;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
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 embassy_time::Timer;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
info!("Hello World!");
|
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 dma = p.DMA1_CH1;
|
||||||
let mut vrefint_channel = adc.enable_vrefint().degrade_adc();
|
let mut vrefint_channel = adc.enable_vrefint().degrade_adc();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::adc::{Adc, SampleTime};
|
use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
info!("Adc oversample test");
|
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);
|
adc.set_sample_time(SampleTime::CYCLES1_5);
|
||||||
let mut pin = p.PA1;
|
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