diff options
| -rw-r--r-- | embassy-stm32/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v3.rs | 87 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/adc.rs | 4 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/adc_dma.rs | 4 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/adc_oversampling.rs | 4 | ||||
| -rw-r--r-- | examples/stm32wl/src/bin/adc.rs | 39 |
6 files changed, 132 insertions, 7 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8ed4dbd65..fe2fb64f2 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 25 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels | 25 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels |
| 26 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled | 26 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled |
| 27 | - fix: Fix stm32h7rs init when using external flash via XSPI | 27 | - fix: Fix stm32h7rs init when using external flash via XSPI |
| 28 | - feat: Add Adc::new_with_clock() to configure analog clock | ||
| 28 | - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) | 29 | - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) |
| 29 | 30 | ||
| 30 | ## 0.3.0 - 2025-08-12 | 31 | ## 0.3.0 - 2025-08-12 |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index dc1faa4d1..77f24c87f 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -107,8 +107,50 @@ pub enum Averaging { | |||
| 107 | Samples128, | 107 | Samples128, |
| 108 | Samples256, | 108 | Samples256, |
| 109 | } | 109 | } |
| 110 | |||
| 111 | cfg_if! { if #[cfg(adc_g0)] { | ||
| 112 | |||
| 113 | /// Synchronous PCLK prescaler | ||
| 114 | /// * ADC_CFGR2:CKMODE in STM32WL5x | ||
| 115 | #[repr(u8)] | ||
| 116 | pub enum CkModePclk { | ||
| 117 | DIV1 = 3, | ||
| 118 | DIV2 = 1, | ||
| 119 | DIV4 = 2, | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Asynchronous ADCCLK prescaler | ||
| 123 | /// * ADC_CCR:PRESC in STM32WL5x | ||
| 124 | #[repr(u8)] | ||
| 125 | pub enum Presc { | ||
| 126 | DIV1, | ||
| 127 | DIV2, | ||
| 128 | DIV4, | ||
| 129 | DIV6, | ||
| 130 | DIV8, | ||
| 131 | DIV10, | ||
| 132 | DIV12, | ||
| 133 | DIV16, | ||
| 134 | DIV32, | ||
| 135 | DIV64, | ||
| 136 | DIV128, | ||
| 137 | DIV256, | ||
| 138 | } | ||
| 139 | |||
| 140 | /// The analog clock is either the synchronous prescaled PCLK or | ||
| 141 | /// the asynchronous prescaled ADCCLK configured by the RCC mux. | ||
| 142 | /// The data sheet states the maximum analog clock frequency - | ||
| 143 | /// for STM32WL55CC it is 36 MHz. | ||
| 144 | pub enum Clock { | ||
| 145 | Sync { div: CkModePclk }, | ||
| 146 | Async { div: Presc }, | ||
| 147 | } | ||
| 148 | |||
| 149 | }} | ||
| 150 | |||
| 110 | impl<'d, T: Instance> Adc<'d, T> { | 151 | impl<'d, T: Instance> Adc<'d, T> { |
| 111 | pub fn new(adc: Peri<'d, T>) -> Self { | 152 | /// Enable the voltage regulator |
| 153 | fn init_regulator() { | ||
| 112 | rcc::enable_and_reset::<T>(); | 154 | rcc::enable_and_reset::<T>(); |
| 113 | T::regs().cr().modify(|reg| { | 155 | T::regs().cr().modify(|reg| { |
| 114 | #[cfg(not(any(adc_g0, adc_u0)))] | 156 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -117,13 +159,17 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 117 | }); | 159 | }); |
| 118 | 160 | ||
| 119 | // If this is false then each ADC_CHSELR bit enables an input channel. | 161 | // If this is false then each ADC_CHSELR bit enables an input channel. |
| 162 | // This is the reset value, so has no effect. | ||
| 120 | #[cfg(any(adc_g0, adc_u0))] | 163 | #[cfg(any(adc_g0, adc_u0))] |
| 121 | T::regs().cfgr1().modify(|reg| { | 164 | T::regs().cfgr1().modify(|reg| { |
| 122 | reg.set_chselrmod(false); | 165 | reg.set_chselrmod(false); |
| 123 | }); | 166 | }); |
| 124 | 167 | ||
| 125 | blocking_delay_us(20); | 168 | blocking_delay_us(20); |
| 169 | } | ||
| 126 | 170 | ||
| 171 | /// Calibrate to remove conversion offset | ||
| 172 | fn init_calibrate() { | ||
| 127 | T::regs().cr().modify(|reg| { | 173 | T::regs().cr().modify(|reg| { |
| 128 | reg.set_adcal(true); | 174 | reg.set_adcal(true); |
| 129 | }); | 175 | }); |
| @@ -133,6 +179,45 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 133 | } | 179 | } |
| 134 | 180 | ||
| 135 | blocking_delay_us(1); | 181 | blocking_delay_us(1); |
| 182 | } | ||
| 183 | |||
| 184 | /// Initialize the ADC leaving any analog clock at reset value. | ||
| 185 | /// For G0 and WL, this is the async clock without prescaler. | ||
| 186 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 187 | Self::init_regulator(); | ||
| 188 | Self::init_calibrate(); | ||
| 189 | Self { | ||
| 190 | adc, | ||
| 191 | sample_time: SampleTime::from_bits(0), | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | #[cfg(adc_g0)] | ||
| 196 | /// Initialize ADC with explicit clock for the analog ADC | ||
| 197 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | ||
| 198 | Self::init_regulator(); | ||
| 199 | |||
| 200 | #[cfg(any(stm32wl5x))] | ||
| 201 | { | ||
| 202 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 203 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 204 | match clock { | ||
| 205 | Clock::Async { div: _ } => { | ||
| 206 | assert!(async_clock_available); | ||
| 207 | } | ||
| 208 | Clock::Sync { div: _ } => { | ||
| 209 | if async_clock_available { | ||
| 210 | warn!("Not using configured ADC clock"); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | } | ||
| 215 | match clock { | ||
| 216 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div as u8)), | ||
| 217 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| reg.set_ckmode(div as u8)), | ||
| 218 | } | ||
| 219 | |||
| 220 | Self::init_calibrate(); | ||
| 136 | 221 | ||
| 137 | Self { | 222 | Self { |
| 138 | adc, | 223 | adc, |
diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs index 6c7f3b48a..7d8653ef2 100644 --- a/examples/stm32g0/src/bin/adc.rs +++ b/examples/stm32g0/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime}; |
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) { | |||
| 12 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 14 | 14 | ||
| 15 | let mut adc = Adc::new(p.ADC1); | 15 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 16 | adc.set_sample_time(SampleTime::CYCLES79_5); | 16 | adc.set_sample_time(SampleTime::CYCLES79_5); |
| 17 | let mut pin = p.PA1; | 17 | let mut pin = p.PA1; |
| 18 | 18 | ||
diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs index d7515933c..8266a6d83 100644 --- a/examples/stm32g0/src/bin/adc_dma.rs +++ b/examples/stm32g0/src/bin/adc_dma.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime}; |
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | 17 | ||
| 18 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| 19 | 19 | ||
| 20 | let mut adc = Adc::new(p.ADC1); | 20 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 21 | 21 | ||
| 22 | let mut dma = p.DMA1_CH1; | 22 | let mut dma = p.DMA1_CH1; |
| 23 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | 23 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); |
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index 9c5dd872a..bc49fac83 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::adc::{Adc, SampleTime}; | 10 | use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Adc oversample test"); | 17 | info!("Adc oversample test"); |
| 18 | 18 | ||
| 19 | let mut adc = Adc::new(p.ADC1); | 19 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 20 | adc.set_sample_time(SampleTime::CYCLES1_5); | 20 | adc.set_sample_time(SampleTime::CYCLES1_5); |
| 21 | let mut pin = p.PA1; | 21 | let mut pin = p.PA1; |
| 22 | 22 | ||
diff --git a/examples/stm32wl/src/bin/adc.rs b/examples/stm32wl/src/bin/adc.rs new file mode 100644 index 000000000..118f02ae1 --- /dev/null +++ b/examples/stm32wl/src/bin/adc.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime}; | ||
| 9 | use embassy_stm32::SharedData; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); | ||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 }); | ||
| 21 | adc.set_sample_time(SampleTime::CYCLES79_5); | ||
| 22 | let mut pin = p.PB2; | ||
| 23 | |||
| 24 | let mut vrefint = adc.enable_vrefint(); | ||
| 25 | let vrefint_sample = adc.blocking_read(&mut vrefint); | ||
| 26 | let convert_to_millivolts = |sample| { | ||
| 27 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf | ||
| 28 | // 6.3.3 Embedded internal reference voltage | ||
| 29 | const VREFINT_MV: u32 = 1212; // mV | ||
| 30 | |||
| 31 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 32 | }; | ||
| 33 | |||
| 34 | loop { | ||
| 35 | let v = adc.blocking_read(&mut pin); | ||
| 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | ||
| 37 | Timer::after_millis(100).await; | ||
| 38 | } | ||
| 39 | } | ||
