diff options
| author | xoviat <[email protected]> | 2025-11-12 13:14:15 -0600 |
|---|---|---|
| committer | xoviat <[email protected]> | 2025-11-12 13:14:15 -0600 |
| commit | e32f78fde6f8130f1eb3effa131e42b7ca153ba6 (patch) | |
| tree | 615ea29191266cd34dcf7bb7b259204e2a125abe | |
| parent | 2d73fe88935bcc42452907b42a7de5a9fa5ab1f8 (diff) | |
stm32/adc: extract into common
add common low-level interface for adc
| -rw-r--r-- | embassy-stm32/src/adc/adc4.rs | 110 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/g4.rs | 459 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/injected.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 173 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/ringbuffered.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v2.rs | 233 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v3.rs | 738 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v4.rs | 327 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/adc.rs | 2 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/adc_dma.rs | 8 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/adc_oversampling.rs | 14 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc.rs | 4 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc_differential.rs | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc_dma.rs | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc_injected_and_regular.rs | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc_oversampling.rs | 13 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/adc.rs | 9 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/adc_dma.rs | 5 | ||||
| -rw-r--r-- | examples/stm32u0/src/bin/adc.rs | 7 | ||||
| -rw-r--r-- | examples/stm32u5/src/bin/adc.rs | 16 |
20 files changed, 869 insertions, 1259 deletions
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index befa8ed4a..04d976513 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -4,7 +4,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR | |||
| 4 | #[cfg(stm32wba)] | 4 | #[cfg(stm32wba)] |
| 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; | 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; |
| 6 | 6 | ||
| 7 | use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; | 7 | use super::{AdcChannel, AnyAdcChannel, RxDma4, blocking_delay_us}; |
| 8 | use crate::dma::Transfer; | 8 | use crate::dma::Transfer; |
| 9 | #[cfg(stm32u5)] | 9 | #[cfg(stm32u5)] |
| 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; | 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; |
| @@ -24,56 +24,24 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 25 | pub const VREF_CALIB_MV: u32 = 3300; | 25 | pub const VREF_CALIB_MV: u32 = 3300; |
| 26 | 26 | ||
| 27 | const VREF_CHANNEL: u8 = 0; | 27 | impl<'d, T: Instance> super::SealedSpecialConverter<super::VrefInt> for Adc4<'d, T> { |
| 28 | const VCORE_CHANNEL: u8 = 12; | 28 | const CHANNEL: u8 = 0; |
| 29 | const TEMP_CHANNEL: u8 = 13; | ||
| 30 | const VBAT_CHANNEL: u8 = 14; | ||
| 31 | const DAC_CHANNEL: u8 = 21; | ||
| 32 | |||
| 33 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||
| 34 | /// Internal voltage reference channel. | ||
| 35 | pub struct VrefInt; | ||
| 36 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 37 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 38 | fn channel(&self) -> u8 { | ||
| 39 | VREF_CHANNEL | ||
| 40 | } | ||
| 41 | } | 29 | } |
| 42 | 30 | ||
| 43 | /// Internal temperature channel. | 31 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Temperature> for Adc4<'d, T> { |
| 44 | pub struct Temperature; | 32 | const CHANNEL: u8 = 13; |
| 45 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 46 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 47 | fn channel(&self) -> u8 { | ||
| 48 | TEMP_CHANNEL | ||
| 49 | } | ||
| 50 | } | 33 | } |
| 51 | 34 | ||
| 52 | /// Internal battery voltage channel. | 35 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Vcore> for Adc4<'d, T> { |
| 53 | pub struct Vbat; | 36 | const CHANNEL: u8 = 12; |
| 54 | impl<T: Instance> AdcChannel<T> for Vbat {} | ||
| 55 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | ||
| 56 | fn channel(&self) -> u8 { | ||
| 57 | VBAT_CHANNEL | ||
| 58 | } | ||
| 59 | } | 37 | } |
| 60 | 38 | ||
| 61 | /// Internal DAC channel. | 39 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Vbat> for Adc4<'d, T> { |
| 62 | pub struct Dac; | 40 | const CHANNEL: u8 = 14; |
| 63 | impl<T: Instance> AdcChannel<T> for Dac {} | ||
| 64 | impl<T: Instance> SealedAdcChannel<T> for Dac { | ||
| 65 | fn channel(&self) -> u8 { | ||
| 66 | DAC_CHANNEL | ||
| 67 | } | ||
| 68 | } | 41 | } |
| 69 | 42 | ||
| 70 | /// Internal Vcore channel. | 43 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Dac> for Adc4<'d, T> { |
| 71 | pub struct Vcore; | 44 | const CHANNEL: u8 = 21; |
| 72 | impl<T: Instance> AdcChannel<T> for Vcore {} | ||
| 73 | impl<T: Instance> SealedAdcChannel<T> for Vcore { | ||
| 74 | fn channel(&self) -> u8 { | ||
| 75 | VCORE_CHANNEL | ||
| 76 | } | ||
| 77 | } | 45 | } |
| 78 | 46 | ||
| 79 | #[derive(Copy, Clone)] | 47 | #[derive(Copy, Clone)] |
| @@ -214,20 +182,6 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 214 | ); | 182 | ); |
| 215 | } | 183 | } |
| 216 | 184 | ||
| 217 | let mut s = Self { adc }; | ||
| 218 | |||
| 219 | s.power_up(); | ||
| 220 | |||
| 221 | s.calibrate(); | ||
| 222 | blocking_delay_us(1); | ||
| 223 | |||
| 224 | s.enable(); | ||
| 225 | s.configure(); | ||
| 226 | |||
| 227 | s | ||
| 228 | } | ||
| 229 | |||
| 230 | fn power_up(&mut self) { | ||
| 231 | T::regs().isr().modify(|w| { | 185 | T::regs().isr().modify(|w| { |
| 232 | w.set_ldordy(true); | 186 | w.set_ldordy(true); |
| 233 | }); | 187 | }); |
| @@ -239,22 +193,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 239 | T::regs().isr().modify(|w| { | 193 | T::regs().isr().modify(|w| { |
| 240 | w.set_ldordy(true); | 194 | w.set_ldordy(true); |
| 241 | }); | 195 | }); |
| 242 | } | ||
| 243 | 196 | ||
| 244 | fn calibrate(&mut self) { | ||
| 245 | T::regs().cr().modify(|w| w.set_adcal(true)); | 197 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 246 | while T::regs().cr().read().adcal() {} | 198 | while T::regs().cr().read().adcal() {} |
| 247 | T::regs().isr().modify(|w| w.set_eocal(true)); | 199 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 248 | } | ||
| 249 | 200 | ||
| 250 | fn enable(&mut self) { | 201 | blocking_delay_us(1); |
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | 202 | |
| 252 | T::regs().cr().modify(|w| w.set_aden(true)); | 203 | Self::enable(); |
| 253 | while !T::regs().isr().read().adrdy() {} | ||
| 254 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 255 | } | ||
| 256 | 204 | ||
| 257 | fn configure(&mut self) { | ||
| 258 | // single conversion mode, software trigger | 205 | // single conversion mode, software trigger |
| 259 | T::regs().cfgr1().modify(|w| { | 206 | T::regs().cfgr1().modify(|w| { |
| 260 | #[cfg(stm32u5)] | 207 | #[cfg(stm32u5)] |
| @@ -280,51 +227,60 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 280 | w.set_smpsel(i, Smpsel::SMP1); | 227 | w.set_smpsel(i, Smpsel::SMP1); |
| 281 | } | 228 | } |
| 282 | }); | 229 | }); |
| 230 | |||
| 231 | Self { adc } | ||
| 232 | } | ||
| 233 | |||
| 234 | fn enable() { | ||
| 235 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 236 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 237 | while !T::regs().isr().read().adrdy() {} | ||
| 238 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 283 | } | 239 | } |
| 284 | 240 | ||
| 285 | /// Enable reading the voltage reference internal channel. | 241 | /// Enable reading the voltage reference internal channel. |
| 286 | pub fn enable_vrefint(&self) -> VrefInt { | 242 | pub fn enable_vrefint(&self) -> super::VrefInt { |
| 287 | T::regs().ccr().modify(|w| { | 243 | T::regs().ccr().modify(|w| { |
| 288 | w.set_vrefen(true); | 244 | w.set_vrefen(true); |
| 289 | }); | 245 | }); |
| 290 | 246 | ||
| 291 | VrefInt {} | 247 | super::VrefInt {} |
| 292 | } | 248 | } |
| 293 | 249 | ||
| 294 | /// Enable reading the temperature internal channel. | 250 | /// Enable reading the temperature internal channel. |
| 295 | pub fn enable_temperature(&self) -> Temperature { | 251 | pub fn enable_temperature(&self) -> super::Temperature { |
| 296 | T::regs().ccr().modify(|w| { | 252 | T::regs().ccr().modify(|w| { |
| 297 | w.set_vsensesel(true); | 253 | w.set_vsensesel(true); |
| 298 | }); | 254 | }); |
| 299 | 255 | ||
| 300 | Temperature {} | 256 | super::Temperature {} |
| 301 | } | 257 | } |
| 302 | 258 | ||
| 303 | /// Enable reading the vbat internal channel. | 259 | /// Enable reading the vbat internal channel. |
| 304 | #[cfg(stm32u5)] | 260 | #[cfg(stm32u5)] |
| 305 | pub fn enable_vbat(&self) -> Vbat { | 261 | pub fn enable_vbat(&self) -> super::Vbat { |
| 306 | T::regs().ccr().modify(|w| { | 262 | T::regs().ccr().modify(|w| { |
| 307 | w.set_vbaten(true); | 263 | w.set_vbaten(true); |
| 308 | }); | 264 | }); |
| 309 | 265 | ||
| 310 | Vbat {} | 266 | super::Vbat {} |
| 311 | } | 267 | } |
| 312 | 268 | ||
| 313 | /// Enable reading the vbat internal channel. | 269 | /// Enable reading the vbat internal channel. |
| 314 | pub fn enable_vcore(&self) -> Vcore { | 270 | pub fn enable_vcore(&self) -> super::Vcore { |
| 315 | Vcore {} | 271 | super::Vcore {} |
| 316 | } | 272 | } |
| 317 | 273 | ||
| 318 | /// Enable reading the vbat internal channel. | 274 | /// Enable reading the vbat internal channel. |
| 319 | #[cfg(stm32u5)] | 275 | #[cfg(stm32u5)] |
| 320 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 276 | pub fn enable_dac_channel(&self, dac: DacChannel) -> super::Dac { |
| 321 | let mux; | 277 | let mux; |
| 322 | match dac { | 278 | match dac { |
| 323 | DacChannel::OUT1 => mux = false, | 279 | DacChannel::OUT1 => mux = false, |
| 324 | DacChannel::OUT2 => mux = true, | 280 | DacChannel::OUT2 => mux = true, |
| 325 | } | 281 | } |
| 326 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 282 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 327 | Dac {} | 283 | super::Dac {} |
| 328 | } | 284 | } |
| 329 | 285 | ||
| 330 | /// Set the ADC resolution. | 286 | /// Set the ADC resolution. |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 6430b0243..0a9f35a5b 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 3 | #[allow(unused)] | 1 | #[allow(unused)] |
| 4 | #[cfg(stm32h7)] | 2 | #[cfg(stm32h7)] |
| 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| @@ -10,15 +8,14 @@ pub use pac::adccommon::vals::Presc; | |||
| 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 8 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | 9 | pub use stm32_metapac::adccommon::vals::Dual; |
| 12 | 10 | ||
| 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; | 11 | use super::{ |
| 12 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, | ||
| 13 | blocking_delay_us, | ||
| 14 | }; | ||
| 14 | use crate::adc::SealedAdcChannel; | 15 | use crate::adc::SealedAdcChannel; |
| 15 | use crate::dma::Transfer; | ||
| 16 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 17 | use crate::{Peri, pac, rcc}; | 17 | use crate::{Peri, pac, rcc}; |
| 18 | 18 | ||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | 19 | mod injected; |
| 23 | pub use injected::InjectedAdc; | 20 | pub use injected::InjectedAdc; |
| 24 | 21 | ||
| @@ -103,6 +100,19 @@ impl Prescaler { | |||
| 103 | } | 100 | } |
| 104 | } | 101 | } |
| 105 | 102 | ||
| 103 | /// ADC configuration | ||
| 104 | #[derive(Default)] | ||
| 105 | pub struct AdcConfig { | ||
| 106 | pub dual_mode: Option<Dual>, | ||
| 107 | pub resolution: Option<Resolution>, | ||
| 108 | #[cfg(stm32g4)] | ||
| 109 | pub oversampling_shift: Option<u8>, | ||
| 110 | #[cfg(stm32g4)] | ||
| 111 | pub oversampling_ratio: Option<u8>, | ||
| 112 | #[cfg(stm32g4)] | ||
| 113 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 114 | } | ||
| 115 | |||
| 106 | // Trigger source for ADC conversions¨ | 116 | // Trigger source for ADC conversions¨ |
| 107 | #[derive(Copy, Clone)] | 117 | #[derive(Copy, Clone)] |
| 108 | pub struct ConversionTrigger { | 118 | pub struct ConversionTrigger { |
| @@ -112,18 +122,9 @@ pub struct ConversionTrigger { | |||
| 112 | pub edge: Exten, | 122 | pub edge: Exten, |
| 113 | } | 123 | } |
| 114 | 124 | ||
| 115 | // Conversion mode for regular ADC channels | ||
| 116 | #[derive(Copy, Clone)] | ||
| 117 | pub enum RegularConversionMode { | ||
| 118 | // Samples as fast as possible | ||
| 119 | Continuous, | ||
| 120 | // Sample at rate determined by external trigger | ||
| 121 | Triggered(ConversionTrigger), | ||
| 122 | } | ||
| 123 | |||
| 124 | impl<'d, T: Instance> Adc<'d, T> { | 125 | impl<'d, T: Instance> Adc<'d, T> { |
| 125 | /// Create a new ADC driver. | 126 | /// Create a new ADC driver. |
| 126 | pub fn new(adc: Peri<'d, T>) -> Self { | 127 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 127 | rcc::enable_and_reset::<T>(); | 128 | rcc::enable_and_reset::<T>(); |
| 128 | 129 | ||
| 129 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 130 | let prescaler = Prescaler::from_ker_ck(T::frequency()); |
| @@ -181,10 +182,38 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 181 | w.set_exten(Exten::DISABLED); | 182 | w.set_exten(Exten::DISABLED); |
| 182 | }); | 183 | }); |
| 183 | 184 | ||
| 185 | if let Some(dual) = config.dual_mode { | ||
| 186 | T::common_regs().ccr().modify(|reg| { | ||
| 187 | reg.set_dual(dual); | ||
| 188 | }) | ||
| 189 | } | ||
| 190 | |||
| 191 | if let Some(resolution) = config.resolution { | ||
| 192 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 193 | } | ||
| 194 | |||
| 195 | #[cfg(stm32g4)] | ||
| 196 | if let Some(shift) = config.oversampling_shift { | ||
| 197 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 198 | } | ||
| 199 | |||
| 200 | #[cfg(stm32g4)] | ||
| 201 | if let Some(ratio) = config.oversampling_ratio { | ||
| 202 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 203 | } | ||
| 204 | |||
| 205 | #[cfg(stm32g4)] | ||
| 206 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 207 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 208 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 209 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 210 | } | ||
| 211 | |||
| 184 | Self { adc } | 212 | Self { adc } |
| 185 | } | 213 | } |
| 186 | 214 | ||
| 187 | fn enable() { | 215 | /// Enable the ADC |
| 216 | pub(super) fn enable() { | ||
| 188 | // Make sure bits are off | 217 | // Make sure bits are off |
| 189 | while T::regs().cr().read().addis() { | 218 | while T::regs().cr().read().addis() { |
| 190 | // spin | 219 | // spin |
| @@ -205,82 +234,33 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 205 | } | 234 | } |
| 206 | } | 235 | } |
| 207 | 236 | ||
| 208 | /// Enable reading the voltage reference internal channel. | 237 | /// Start regular adc conversion |
| 209 | pub fn enable_vrefint(&self) -> super::VrefInt | 238 | pub(super) fn start() { |
| 210 | where | 239 | T::regs().cr().modify(|reg| { |
| 211 | T: super::SpecialConverter<super::VrefInt>, | 240 | reg.set_adstart(true); |
| 212 | { | ||
| 213 | T::common_regs().ccr().modify(|reg| { | ||
| 214 | reg.set_vrefen(true); | ||
| 215 | }); | 241 | }); |
| 216 | |||
| 217 | super::VrefInt {} | ||
| 218 | } | 242 | } |
| 219 | 243 | ||
| 220 | /// Enable reading the temperature internal channel. | 244 | /// Stop regular conversions and disable DMA |
| 221 | pub fn enable_temperature(&self) -> super::Temperature | 245 | pub(super) fn stop() { |
| 222 | where | 246 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 223 | T: super::SpecialConverter<super::Temperature>, | 247 | T::regs().cr().modify(|reg| { |
| 224 | { | 248 | reg.set_adstp(Adstp::STOP); |
| 225 | T::common_regs().ccr().modify(|reg| { | 249 | }); |
| 226 | reg.set_vsenseen(true); | 250 | // The software must poll ADSTART until the bit is reset before assuming the |
| 227 | }); | 251 | // ADC is completely stopped |
| 228 | 252 | while T::regs().cr().read().adstart() {} | |
| 229 | super::Temperature {} | 253 | } |
| 230 | } | ||
| 231 | 254 | ||
| 232 | /// Enable reading the vbat internal channel. | 255 | // Disable dma control and continuous conversion, if enabled |
| 233 | pub fn enable_vbat(&self) -> super::Vbat | 256 | T::regs().cfgr().modify(|reg| { |
| 234 | where | 257 | reg.set_cont(false); |
| 235 | T: super::SpecialConverter<super::Vbat>, | 258 | reg.set_dmaen(Dmaen::DISABLE); |
| 236 | { | ||
| 237 | T::common_regs().ccr().modify(|reg| { | ||
| 238 | reg.set_vbaten(true); | ||
| 239 | }); | 259 | }); |
| 240 | |||
| 241 | super::Vbat {} | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Set oversampling shift. | ||
| 245 | #[cfg(stm32g4)] | ||
| 246 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 247 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 248 | } | ||
| 249 | |||
| 250 | /// Set oversampling ratio. | ||
| 251 | #[cfg(stm32g4)] | ||
| 252 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 253 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Enable oversampling in regular mode. | ||
| 257 | #[cfg(stm32g4)] | ||
| 258 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 259 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 260 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 261 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 262 | } | ||
| 263 | |||
| 264 | // Reads that are not implemented as INJECTED in "blocking_read" | ||
| 265 | // #[cfg(stm32g4)] | ||
| 266 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 267 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 268 | // } | ||
| 269 | |||
| 270 | // #[cfg(stm32g4)] | ||
| 271 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { | ||
| 272 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 273 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 274 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 275 | // } | ||
| 276 | |||
| 277 | /// Set the ADC resolution. | ||
| 278 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 279 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 280 | } | 260 | } |
| 281 | 261 | ||
| 282 | /// Perform a single conversion. | 262 | /// Perform a single conversion. |
| 283 | fn convert(&mut self) -> u16 { | 263 | pub(super) fn convert() -> u16 { |
| 284 | T::regs().isr().modify(|reg| { | 264 | T::regs().isr().modify(|reg| { |
| 285 | reg.set_eos(true); | 265 | reg.set_eos(true); |
| 286 | reg.set_eoc(true); | 266 | reg.set_eoc(true); |
| @@ -298,136 +278,42 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 298 | T::regs().dr().read().0 as u16 | 278 | T::regs().dr().read().0 as u16 |
| 299 | } | 279 | } |
| 300 | 280 | ||
| 301 | /// Read an ADC pin. | 281 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { |
| 302 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 303 | channel.setup(); | ||
| 304 | |||
| 305 | Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 306 | |||
| 307 | #[cfg(stm32h7)] | ||
| 308 | { | ||
| 309 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 310 | T::regs() | ||
| 311 | .pcsel() | ||
| 312 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 313 | } | ||
| 314 | |||
| 315 | self.convert() | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Start regular adc conversion | ||
| 319 | pub(super) fn start() { | ||
| 320 | T::regs().cr().modify(|reg| { | ||
| 321 | reg.set_adstart(true); | ||
| 322 | }); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Stop regular conversions | ||
| 326 | pub(super) fn stop() { | ||
| 327 | Self::stop_regular_conversions(); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Teardown method for stopping regular ADC conversions | ||
| 331 | pub(super) fn teardown_dma() { | ||
| 332 | Self::stop_regular_conversions(); | ||
| 333 | |||
| 334 | // Disable dma control | ||
| 335 | T::regs().cfgr().modify(|reg| { | ||
| 336 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 337 | }); | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Read one or multiple ADC regular channels using DMA. | ||
| 341 | /// | ||
| 342 | /// `sequence` iterator and `readings` must have the same length. | ||
| 343 | /// | ||
| 344 | /// Example | ||
| 345 | /// ```rust,ignore | ||
| 346 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 347 | /// | ||
| 348 | /// let mut adc = Adc::new(p.ADC1); | ||
| 349 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 350 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 351 | /// let mut measurements = [0u16; 2]; | ||
| 352 | /// | ||
| 353 | /// adc.read( | ||
| 354 | /// p.DMA1_CH2.reborrow(), | ||
| 355 | /// [ | ||
| 356 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 357 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 358 | /// ] | ||
| 359 | /// .into_iter(), | ||
| 360 | /// &mut measurements, | ||
| 361 | /// ) | ||
| 362 | /// .await; | ||
| 363 | /// defmt::info!("measurements: {}", measurements); | ||
| 364 | /// ``` | ||
| 365 | /// | ||
| 366 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 367 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 368 | pub async fn read( | ||
| 369 | &mut self, | ||
| 370 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 371 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 372 | readings: &mut [u16], | ||
| 373 | ) { | ||
| 374 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 375 | assert!( | ||
| 376 | sequence.len() == readings.len(), | ||
| 377 | "Sequence length must be equal to readings length" | ||
| 378 | ); | ||
| 379 | assert!( | ||
| 380 | sequence.len() <= 16, | ||
| 381 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 382 | ); | ||
| 383 | |||
| 384 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 385 | Self::stop_regular_conversions(); | ||
| 386 | Self::enable(); | ||
| 387 | |||
| 388 | Self::configure_sequence( | ||
| 389 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 390 | ); | ||
| 391 | |||
| 392 | // Set continuous mode with oneshot dma. | ||
| 393 | // Clear overrun flag before starting transfer. | ||
| 394 | T::regs().isr().modify(|reg| { | 282 | T::regs().isr().modify(|reg| { |
| 395 | reg.set_ovr(true); | 283 | reg.set_ovr(true); |
| 396 | }); | 284 | }); |
| 397 | 285 | ||
| 398 | T::regs().cfgr().modify(|reg| { | 286 | T::regs().cfgr().modify(|reg| { |
| 399 | reg.set_discen(false); | 287 | reg.set_discen(false); // Convert all channels for each trigger |
| 400 | reg.set_cont(true); | 288 | reg.set_dmacfg(match conversion_mode { |
| 401 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 289 | ConversionMode::Singular => Dmacfg::ONE_SHOT, |
| 290 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 291 | }); | ||
| 402 | reg.set_dmaen(Dmaen::ENABLE); | 292 | reg.set_dmaen(Dmaen::ENABLE); |
| 403 | }); | 293 | }); |
| 404 | 294 | ||
| 405 | let request = rx_dma.request(); | 295 | if let ConversionMode::Repeated(mode) = conversion_mode { |
| 406 | let transfer = unsafe { | 296 | match mode { |
| 407 | Transfer::new_read( | 297 | RegularConversionMode::Continuous => { |
| 408 | rx_dma, | 298 | T::regs().cfgr().modify(|reg| { |
| 409 | request, | 299 | reg.set_cont(true); |
| 410 | T::regs().dr().as_ptr() as *mut u16, | 300 | }); |
| 411 | readings, | 301 | } |
| 412 | Default::default(), | 302 | RegularConversionMode::Triggered(trigger) => { |
| 413 | ) | 303 | T::regs().cfgr().modify(|r| { |
| 414 | }; | 304 | r.set_cont(false); // New trigger is neede for each sample to be read |
| 415 | 305 | }); | |
| 416 | // Start conversion | ||
| 417 | T::regs().cr().modify(|reg| { | ||
| 418 | reg.set_adstart(true); | ||
| 419 | }); | ||
| 420 | |||
| 421 | // Wait for conversion sequence to finish. | ||
| 422 | transfer.await; | ||
| 423 | 306 | ||
| 424 | // Ensure conversions are finished. | 307 | T::regs().cfgr().modify(|r| { |
| 425 | Self::stop_regular_conversions(); | 308 | r.set_extsel(trigger.channel); |
| 309 | r.set_exten(trigger.edge); | ||
| 310 | }); | ||
| 426 | 311 | ||
| 427 | // Reset configuration. | 312 | // Regular conversions uses DMA so no need to generate interrupt |
| 428 | T::regs().cfgr().modify(|reg| { | 313 | T::regs().ier().modify(|r| r.set_eosie(false)); |
| 429 | reg.set_cont(false); | 314 | } |
| 430 | }); | 315 | } |
| 316 | } | ||
| 431 | } | 317 | } |
| 432 | 318 | ||
| 433 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | 319 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| @@ -489,95 +375,54 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 489 | } | 375 | } |
| 490 | } | 376 | } |
| 491 | 377 | ||
| 492 | /// Set external trigger for regular conversion sequence | 378 | /// Enable reading the voltage reference internal channel. |
| 493 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | 379 | pub fn enable_vrefint(&self) -> super::VrefInt |
| 494 | T::regs().cfgr().modify(|r| { | 380 | where |
| 495 | r.set_extsel(trigger.channel); | 381 | T: super::SpecialConverter<super::VrefInt>, |
| 496 | r.set_exten(trigger.edge); | 382 | { |
| 383 | T::common_regs().ccr().modify(|reg| { | ||
| 384 | reg.set_vrefen(true); | ||
| 497 | }); | 385 | }); |
| 498 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 499 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 500 | } | ||
| 501 | 386 | ||
| 502 | // Dual ADC mode selection | 387 | super::VrefInt {} |
| 503 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 504 | T::common_regs().ccr().modify(|reg| { | ||
| 505 | reg.set_dual(val); | ||
| 506 | }) | ||
| 507 | } | 388 | } |
| 508 | 389 | ||
| 509 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 390 | /// Enable reading the temperature internal channel. |
| 510 | /// | 391 | pub fn enable_temperature(&self) -> super::Temperature |
| 511 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | 392 | where |
| 512 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | 393 | T: super::SpecialConverter<super::Temperature>, |
| 513 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | 394 | { |
| 514 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | 395 | T::common_regs().ccr().modify(|reg| { |
| 515 | /// defines the period at which the buffer should be read. | 396 | reg.set_vsenseen(true); |
| 516 | /// | ||
| 517 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 518 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 519 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 520 | /// the buffer length should be `3 * 40 = 120`. | ||
| 521 | /// | ||
| 522 | /// # Parameters | ||
| 523 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 524 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 525 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 526 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 527 | /// | ||
| 528 | /// # Returns | ||
| 529 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 530 | pub fn into_ring_buffered<'a>( | ||
| 531 | mut self, | ||
| 532 | dma: Peri<'a, impl RxDma<T>>, | ||
| 533 | dma_buf: &'a mut [u16], | ||
| 534 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 535 | mode: RegularConversionMode, | ||
| 536 | ) -> RingBufferedAdc<'a, T> { | ||
| 537 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 538 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 539 | assert!( | ||
| 540 | sequence.len() <= 16, | ||
| 541 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 542 | ); | ||
| 543 | // reset conversions and enable the adc | ||
| 544 | Self::stop_regular_conversions(); | ||
| 545 | Self::enable(); | ||
| 546 | |||
| 547 | //adc side setup | ||
| 548 | Self::configure_sequence( | ||
| 549 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 550 | ); | ||
| 551 | |||
| 552 | // Clear overrun flag before starting transfer. | ||
| 553 | T::regs().isr().modify(|reg| { | ||
| 554 | reg.set_ovr(true); | ||
| 555 | }); | 397 | }); |
| 556 | 398 | ||
| 557 | T::regs().cfgr().modify(|reg| { | 399 | super::Temperature {} |
| 558 | reg.set_discen(false); // Convert all channels for each trigger | 400 | } |
| 559 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 401 | |
| 560 | reg.set_dmaen(Dmaen::ENABLE); | 402 | /// Enable reading the vbat internal channel. |
| 403 | pub fn enable_vbat(&self) -> super::Vbat | ||
| 404 | where | ||
| 405 | T: super::SpecialConverter<super::Vbat>, | ||
| 406 | { | ||
| 407 | T::common_regs().ccr().modify(|reg| { | ||
| 408 | reg.set_vbaten(true); | ||
| 561 | }); | 409 | }); |
| 562 | 410 | ||
| 563 | match mode { | 411 | super::Vbat {} |
| 564 | RegularConversionMode::Continuous => { | 412 | } |
| 565 | T::regs().cfgr().modify(|reg| { | ||
| 566 | reg.set_cont(true); | ||
| 567 | }); | ||
| 568 | } | ||
| 569 | RegularConversionMode::Triggered(trigger) => { | ||
| 570 | T::regs().cfgr().modify(|r| { | ||
| 571 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 572 | }); | ||
| 573 | self.set_regular_conversion_trigger(trigger); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | 413 | ||
| 577 | mem::forget(self); | 414 | // Reads that are not implemented as INJECTED in "blocking_read" |
| 415 | // #[cfg(stm32g4)] | ||
| 416 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 417 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 418 | // } | ||
| 578 | 419 | ||
| 579 | RingBufferedAdc::new(dma, dma_buf) | 420 | // #[cfg(stm32g4)] |
| 580 | } | 421 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { |
| 422 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 423 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 424 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 425 | // } | ||
| 581 | 426 | ||
| 582 | /// Configures the ADC for injected conversions. | 427 | /// Configures the ADC for injected conversions. |
| 583 | /// | 428 | /// |
| @@ -607,7 +452,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 607 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | 452 | /// - Accessing samples beyond `N` will result in a panic; use the returned type |
| 608 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | 453 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 609 | pub fn setup_injected_conversions<'a, const N: usize>( | 454 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 610 | mut self, | 455 | self, |
| 611 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | 456 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 612 | trigger: ConversionTrigger, | 457 | trigger: ConversionTrigger, |
| 613 | interrupt: bool, | 458 | interrupt: bool, |
| @@ -619,7 +464,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 619 | NR_INJECTED_RANKS | 464 | NR_INJECTED_RANKS |
| 620 | ); | 465 | ); |
| 621 | 466 | ||
| 622 | Self::stop_regular_conversions(); | 467 | Self::stop(); |
| 623 | Self::enable(); | 468 | Self::enable(); |
| 624 | 469 | ||
| 625 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); | 470 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| @@ -649,8 +494,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 649 | 494 | ||
| 650 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | 495 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 651 | 496 | ||
| 652 | self.set_injected_conversion_trigger(trigger); | 497 | // Set external trigger for injected conversion sequence |
| 653 | self.enable_injected_eos_interrupt(interrupt); | 498 | // Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 499 | T::regs().jsqr().modify(|r| { | ||
| 500 | r.set_jextsel(trigger.channel); | ||
| 501 | r.set_jexten(trigger.edge); | ||
| 502 | }); | ||
| 503 | |||
| 504 | // Enable end of injected sequence interrupt | ||
| 505 | T::regs().ier().modify(|r| r.set_jeosie(interrupt)); | ||
| 506 | |||
| 654 | Self::start_injected_conversions(); | 507 | Self::start_injected_conversions(); |
| 655 | 508 | ||
| 656 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | 509 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| @@ -688,7 +541,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 688 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | 541 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 689 | injected_trigger: ConversionTrigger, | 542 | injected_trigger: ConversionTrigger, |
| 690 | injected_interrupt: bool, | 543 | injected_interrupt: bool, |
| 691 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | 544 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { |
| 692 | unsafe { | 545 | unsafe { |
| 693 | ( | 546 | ( |
| 694 | Self { | 547 | Self { |
| @@ -721,32 +574,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 721 | reg.set_jadstart(true); | 574 | reg.set_jadstart(true); |
| 722 | }); | 575 | }); |
| 723 | } | 576 | } |
| 724 | |||
| 725 | /// Set external trigger for injected conversion sequence | ||
| 726 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | ||
| 727 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 728 | T::regs().jsqr().modify(|r| { | ||
| 729 | r.set_jextsel(trigger.channel); | ||
| 730 | r.set_jexten(trigger.edge); | ||
| 731 | }); | ||
| 732 | } | ||
| 733 | |||
| 734 | /// Enable end of injected sequence interrupt | ||
| 735 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 736 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 737 | } | ||
| 738 | |||
| 739 | // Stop regular conversions | ||
| 740 | fn stop_regular_conversions() { | ||
| 741 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 742 | T::regs().cr().modify(|reg| { | ||
| 743 | reg.set_adstp(Adstp::STOP); | ||
| 744 | }); | ||
| 745 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 746 | // ADC is completely stopped | ||
| 747 | while T::regs().cr().read().adstart() {} | ||
| 748 | } | ||
| 749 | } | ||
| 750 | } | 577 | } |
| 751 | 578 | ||
| 752 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 579 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index f9f1bba2a..7bb3a541c 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -38,7 +38,7 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> { | |||
| 38 | 38 | ||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { |
| 40 | fn drop(&mut self) { | 40 | fn drop(&mut self) { |
| 41 | Adc::<T>::teardown_dma(); | 41 | Adc::<T>::stop(); |
| 42 | compiler_fence(Ordering::SeqCst); | 42 | compiler_fence(Ordering::SeqCst); |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e321c4fa1..bf404d6ef 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -17,6 +17,9 @@ | |||
| 17 | #[cfg_attr(adc_c0, path = "c0.rs")] | 17 | #[cfg_attr(adc_c0, path = "c0.rs")] |
| 18 | mod _version; | 18 | mod _version; |
| 19 | 19 | ||
| 20 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 21 | mod ringbuffered; | ||
| 22 | |||
| 20 | use core::marker::PhantomData; | 23 | use core::marker::PhantomData; |
| 21 | 24 | ||
| 22 | #[allow(unused)] | 25 | #[allow(unused)] |
| @@ -25,6 +28,8 @@ pub use _version::*; | |||
| 25 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 29 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 30 | use embassy_sync::waitqueue::AtomicWaker; |
| 31 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 32 | pub use ringbuffered::RingBufferedAdc; | ||
| 28 | 33 | ||
| 29 | #[cfg(any(adc_u5, adc_wba))] | 34 | #[cfg(any(adc_u5, adc_wba))] |
| 30 | #[path = "adc4.rs"] | 35 | #[path = "adc4.rs"] |
| @@ -105,6 +110,166 @@ pub(crate) fn blocking_delay_us(us: u32) { | |||
| 105 | } | 110 | } |
| 106 | } | 111 | } |
| 107 | 112 | ||
| 113 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 114 | pub(self) enum ConversionMode { | ||
| 115 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 116 | Singular, | ||
| 117 | #[allow(dead_code)] | ||
| 118 | Repeated(RegularConversionMode), | ||
| 119 | } | ||
| 120 | |||
| 121 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 122 | // Conversion mode for regular ADC channels | ||
| 123 | #[derive(Copy, Clone)] | ||
| 124 | pub enum RegularConversionMode { | ||
| 125 | // Samples as fast as possible | ||
| 126 | Continuous, | ||
| 127 | #[cfg(adc_g4)] | ||
| 128 | // Sample at rate determined by external trigger | ||
| 129 | Triggered(ConversionTrigger), | ||
| 130 | } | ||
| 131 | |||
| 132 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 133 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4))] | ||
| 134 | /// Read an ADC pin. | ||
| 135 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 136 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 137 | channel.setup(); | ||
| 138 | |||
| 139 | #[cfg(not(adc_v4))] | ||
| 140 | Self::enable(); | ||
| 141 | Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 142 | |||
| 143 | Self::convert() | ||
| 144 | } | ||
| 145 | |||
| 146 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 147 | /// Read one or multiple ADC regular channels using DMA. | ||
| 148 | /// | ||
| 149 | /// `sequence` iterator and `readings` must have the same length. | ||
| 150 | /// | ||
| 151 | /// Example | ||
| 152 | /// ```rust,ignore | ||
| 153 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 154 | /// | ||
| 155 | /// let mut adc = Adc::new(p.ADC1); | ||
| 156 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 157 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 158 | /// let mut measurements = [0u16; 2]; | ||
| 159 | /// | ||
| 160 | /// adc.read( | ||
| 161 | /// p.DMA1_CH2.reborrow(), | ||
| 162 | /// [ | ||
| 163 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 164 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 165 | /// ] | ||
| 166 | /// .into_iter(), | ||
| 167 | /// &mut measurements, | ||
| 168 | /// ) | ||
| 169 | /// .await; | ||
| 170 | /// defmt::info!("measurements: {}", measurements); | ||
| 171 | /// ``` | ||
| 172 | /// | ||
| 173 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 174 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 175 | pub async fn read( | ||
| 176 | &mut self, | ||
| 177 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | ||
| 178 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 179 | readings: &mut [u16], | ||
| 180 | ) { | ||
| 181 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 182 | assert!( | ||
| 183 | sequence.len() == readings.len(), | ||
| 184 | "Sequence length must be equal to readings length" | ||
| 185 | ); | ||
| 186 | assert!( | ||
| 187 | sequence.len() <= 16, | ||
| 188 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 189 | ); | ||
| 190 | |||
| 191 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 192 | Self::stop(); | ||
| 193 | Self::enable(); | ||
| 194 | |||
| 195 | Self::configure_sequence( | ||
| 196 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 197 | ); | ||
| 198 | |||
| 199 | Self::configure_dma(ConversionMode::Singular); | ||
| 200 | |||
| 201 | let request = rx_dma.request(); | ||
| 202 | let transfer = unsafe { | ||
| 203 | crate::dma::Transfer::new_read( | ||
| 204 | rx_dma, | ||
| 205 | request, | ||
| 206 | T::regs().dr().as_ptr() as *mut u16, | ||
| 207 | readings, | ||
| 208 | Default::default(), | ||
| 209 | ) | ||
| 210 | }; | ||
| 211 | |||
| 212 | Self::start(); | ||
| 213 | |||
| 214 | // Wait for conversion sequence to finish. | ||
| 215 | transfer.await; | ||
| 216 | |||
| 217 | // Ensure conversions are finished. | ||
| 218 | Self::stop(); | ||
| 219 | } | ||
| 220 | |||
| 221 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 222 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 223 | /// | ||
| 224 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 225 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 226 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 227 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 228 | /// defines the period at which the buffer should be read. | ||
| 229 | /// | ||
| 230 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 231 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 232 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 233 | /// the buffer length should be `3 * 40 = 120`. | ||
| 234 | /// | ||
| 235 | /// # Parameters | ||
| 236 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 237 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 238 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 239 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 240 | /// | ||
| 241 | /// # Returns | ||
| 242 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 243 | pub fn into_ring_buffered<'a>( | ||
| 244 | self, | ||
| 245 | dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, | ||
| 246 | dma_buf: &'a mut [u16], | ||
| 247 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 248 | mode: RegularConversionMode, | ||
| 249 | ) -> RingBufferedAdc<'a, T> { | ||
| 250 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 251 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 252 | assert!( | ||
| 253 | sequence.len() <= 16, | ||
| 254 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 255 | ); | ||
| 256 | // reset conversions and enable the adc | ||
| 257 | Self::stop(); | ||
| 258 | Self::enable(); | ||
| 259 | |||
| 260 | //adc side setup | ||
| 261 | Self::configure_sequence( | ||
| 262 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 263 | ); | ||
| 264 | |||
| 265 | Self::configure_dma(ConversionMode::Repeated(mode)); | ||
| 266 | |||
| 267 | core::mem::forget(self); | ||
| 268 | |||
| 269 | RingBufferedAdc::new(dma, dma_buf) | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 108 | pub(self) trait SpecialChannel {} | 273 | pub(self) trait SpecialChannel {} |
| 109 | 274 | ||
| 110 | /// Implemented for ADCs that have a special channel | 275 | /// Implemented for ADCs that have a special channel |
| @@ -143,6 +308,14 @@ impl SpecialChannel for Temperature {} | |||
| 143 | pub struct Vbat; | 308 | pub struct Vbat; |
| 144 | impl SpecialChannel for Vbat {} | 309 | impl SpecialChannel for Vbat {} |
| 145 | 310 | ||
| 311 | /// Vcore channel. | ||
| 312 | pub struct Vcore; | ||
| 313 | impl SpecialChannel for Vcore {} | ||
| 314 | |||
| 315 | /// Internal dac channel. | ||
| 316 | pub struct Dac; | ||
| 317 | impl SpecialChannel for Dac {} | ||
| 318 | |||
| 146 | /// ADC instance. | 319 | /// ADC instance. |
| 147 | #[cfg(not(any( | 320 | #[cfg(not(any( |
| 148 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, | 321 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 024c6acdc..62ea0d3a2 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -172,7 +172,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 172 | 172 | ||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { |
| 174 | fn drop(&mut self) { | 174 | fn drop(&mut self) { |
| 175 | Adc::<T>::teardown_dma(); | 175 | Adc::<T>::stop(); |
| 176 | 176 | ||
| 177 | compiler_fence(Ordering::SeqCst); | 177 | compiler_fence(Ordering::SeqCst); |
| 178 | 178 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index efa1cc68c..2f9fabafb 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,15 +1,11 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | 2 | ||
| 4 | use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 5 | use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; | 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; |
| 6 | use crate::pac::adc::vals; | 5 | use crate::pac::adc::vals; |
| 7 | use crate::time::Hertz; | 6 | use crate::time::Hertz; |
| 8 | use crate::{Peri, rcc}; | 7 | use crate::{Peri, rcc}; |
| 9 | 8 | ||
| 10 | mod ringbuffered; | ||
| 11 | pub use ringbuffered::RingBufferedAdc; | ||
| 12 | |||
| 13 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | 9 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 14 | r.sr().modify(|regs| { | 10 | r.sr().modify(|regs| { |
| 15 | regs.set_eoc(false); | 11 | regs.set_eoc(false); |
| @@ -89,11 +85,21 @@ impl Prescaler { | |||
| 89 | } | 85 | } |
| 90 | } | 86 | } |
| 91 | 87 | ||
| 88 | /// ADC configuration | ||
| 89 | #[derive(Default)] | ||
| 90 | pub struct AdcConfig { | ||
| 91 | resolution: Option<Resolution>, | ||
| 92 | } | ||
| 93 | |||
| 92 | impl<'d, T> Adc<'d, T> | 94 | impl<'d, T> Adc<'d, T> |
| 93 | where | 95 | where |
| 94 | T: Instance, | 96 | T: Instance, |
| 95 | { | 97 | { |
| 96 | pub fn new(adc: Peri<'d, T>) -> Self { | 98 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 99 | Self::new_with_config(adc, Default::default()) | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 97 | rcc::enable_and_reset::<T>(); | 103 | rcc::enable_and_reset::<T>(); |
| 98 | 104 | ||
| 99 | let presc = Prescaler::from_pclk2(T::frequency()); | 105 | let presc = Prescaler::from_pclk2(T::frequency()); |
| @@ -104,84 +110,14 @@ where | |||
| 104 | 110 | ||
| 105 | blocking_delay_us(3); | 111 | blocking_delay_us(3); |
| 106 | 112 | ||
| 107 | Self { adc } | 113 | if let Some(resolution) = config.resolution { |
| 108 | } | 114 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); |
| 109 | 115 | } | |
| 110 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 111 | /// | ||
| 112 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 113 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 114 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 115 | /// | ||
| 116 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 117 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 118 | /// | ||
| 119 | /// [`read`]: #method.read | ||
| 120 | pub fn into_ring_buffered<'a>( | ||
| 121 | self, | ||
| 122 | dma: Peri<'d, impl RxDma<T>>, | ||
| 123 | dma_buf: &'d mut [u16], | ||
| 124 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 125 | ) -> RingBufferedAdc<'d, T> { | ||
| 126 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 127 | |||
| 128 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 129 | channel.setup(); | ||
| 130 | |||
| 131 | (channel.channel, sample_time) | ||
| 132 | })); | ||
| 133 | compiler_fence(Ordering::SeqCst); | ||
| 134 | |||
| 135 | Self::setup_dma(); | ||
| 136 | |||
| 137 | // Don't disable the clock | ||
| 138 | mem::forget(self); | ||
| 139 | |||
| 140 | RingBufferedAdc::new(dma, dma_buf) | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 144 | channel.setup(); | ||
| 145 | |||
| 146 | // Configure ADC | ||
| 147 | let channel = channel.channel(); | ||
| 148 | |||
| 149 | Self::configure_sequence([(channel, sample_time)].into_iter()); | ||
| 150 | Self::blocking_convert() | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | ||
| 154 | /// [Adc::read_internal()] to perform conversion. | ||
| 155 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 156 | T::common_regs().ccr().modify(|reg| { | ||
| 157 | reg.set_tsvrefe(true); | ||
| 158 | }); | ||
| 159 | |||
| 160 | VrefInt {} | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Enables internal temperature sensor and returns [Temperature], which can be used in | ||
| 164 | /// [Adc::read_internal()] to perform conversion. | ||
| 165 | /// | ||
| 166 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 167 | /// temperature sensor will return vbat value. | ||
| 168 | pub fn enable_temperature(&self) -> Temperature { | ||
| 169 | T::common_regs().ccr().modify(|reg| { | ||
| 170 | reg.set_tsvrefe(true); | ||
| 171 | }); | ||
| 172 | 116 | ||
| 173 | Temperature {} | 117 | Self { adc } |
| 174 | } | 118 | } |
| 175 | 119 | ||
| 176 | /// Enables vbat input and returns [Vbat], which can be used in | 120 | pub(super) fn enable() {} |
| 177 | /// [Adc::read_internal()] to perform conversion. | ||
| 178 | pub fn enable_vbat(&self) -> Vbat { | ||
| 179 | T::common_regs().ccr().modify(|reg| { | ||
| 180 | reg.set_vbate(true); | ||
| 181 | }); | ||
| 182 | |||
| 183 | Vbat {} | ||
| 184 | } | ||
| 185 | 121 | ||
| 186 | pub(super) fn start() { | 122 | pub(super) fn start() { |
| 187 | // Begin ADC conversions | 123 | // Begin ADC conversions |
| @@ -192,18 +128,31 @@ where | |||
| 192 | } | 128 | } |
| 193 | 129 | ||
| 194 | pub(super) fn stop() { | 130 | pub(super) fn stop() { |
| 131 | let r = T::regs(); | ||
| 132 | |||
| 195 | // Stop ADC | 133 | // Stop ADC |
| 196 | T::regs().cr2().modify(|reg| { | 134 | r.cr2().modify(|reg| { |
| 197 | // Stop ADC | 135 | // Stop ADC |
| 198 | reg.set_swstart(false); | 136 | reg.set_swstart(false); |
| 137 | // Stop ADC | ||
| 138 | reg.set_adon(false); | ||
| 139 | // Stop DMA | ||
| 140 | reg.set_dma(false); | ||
| 199 | }); | 141 | }); |
| 200 | } | ||
| 201 | 142 | ||
| 202 | pub fn set_resolution(&mut self, resolution: Resolution) { | 143 | r.cr1().modify(|w| { |
| 203 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 144 | // Disable interrupt for end of conversion |
| 145 | w.set_eocie(false); | ||
| 146 | // Disable interrupt for overrun | ||
| 147 | w.set_ovrie(false); | ||
| 148 | }); | ||
| 149 | |||
| 150 | clear_interrupt_flags(r); | ||
| 151 | |||
| 152 | compiler_fence(Ordering::SeqCst); | ||
| 204 | } | 153 | } |
| 205 | 154 | ||
| 206 | pub(super) fn blocking_convert() -> u16 { | 155 | pub(super) fn convert() -> u16 { |
| 207 | // clear end of conversion flag | 156 | // clear end of conversion flag |
| 208 | T::regs().sr().modify(|reg| { | 157 | T::regs().sr().modify(|reg| { |
| 209 | reg.set_eoc(false); | 158 | reg.set_eoc(false); |
| @@ -224,7 +173,44 @@ where | |||
| 224 | T::regs().dr().read().0 as u16 | 173 | T::regs().dr().read().0 as u16 |
| 225 | } | 174 | } |
| 226 | 175 | ||
| 227 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { | 176 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { |
| 177 | match conversion_mode { | ||
| 178 | ConversionMode::Repeated(_) => { | ||
| 179 | let r = T::regs(); | ||
| 180 | |||
| 181 | // Clear all interrupts | ||
| 182 | r.sr().modify(|regs| { | ||
| 183 | regs.set_eoc(false); | ||
| 184 | regs.set_ovr(false); | ||
| 185 | regs.set_strt(false); | ||
| 186 | }); | ||
| 187 | |||
| 188 | r.cr1().modify(|w| { | ||
| 189 | // Enable interrupt for end of conversion | ||
| 190 | w.set_eocie(true); | ||
| 191 | // Enable interrupt for overrun | ||
| 192 | w.set_ovrie(true); | ||
| 193 | // Scanning converisons of multiple channels | ||
| 194 | w.set_scan(true); | ||
| 195 | // Continuous conversion mode | ||
| 196 | w.set_discen(false); | ||
| 197 | }); | ||
| 198 | |||
| 199 | r.cr2().modify(|w| { | ||
| 200 | // Enable DMA mode | ||
| 201 | w.set_dma(true); | ||
| 202 | // Enable continuous conversions | ||
| 203 | w.set_cont(true); | ||
| 204 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 205 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 206 | // EOC flag is set at the end of each conversion. | ||
| 207 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 208 | }); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 228 | T::regs().cr2().modify(|reg| { | 214 | T::regs().cr2().modify(|reg| { |
| 229 | reg.set_adon(true); | 215 | reg.set_adon(true); |
| 230 | }); | 216 | }); |
| @@ -234,7 +220,7 @@ where | |||
| 234 | r.set_l((sequence.len() - 1).try_into().unwrap()); | 220 | r.set_l((sequence.len() - 1).try_into().unwrap()); |
| 235 | }); | 221 | }); |
| 236 | 222 | ||
| 237 | for (i, (ch, sample_time)) in sequence.enumerate() { | 223 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { |
| 238 | // Set the channel in the right sequence field. | 224 | // Set the channel in the right sequence field. |
| 239 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); | 225 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); |
| 240 | 226 | ||
| @@ -247,62 +233,37 @@ where | |||
| 247 | } | 233 | } |
| 248 | } | 234 | } |
| 249 | 235 | ||
| 250 | pub(super) fn setup_dma() { | 236 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| 251 | let r = T::regs(); | 237 | /// [Adc::read_internal()] to perform conversion. |
| 252 | 238 | pub fn enable_vrefint(&self) -> VrefInt { | |
| 253 | // Clear all interrupts | 239 | T::common_regs().ccr().modify(|reg| { |
| 254 | r.sr().modify(|regs| { | 240 | reg.set_tsvrefe(true); |
| 255 | regs.set_eoc(false); | ||
| 256 | regs.set_ovr(false); | ||
| 257 | regs.set_strt(false); | ||
| 258 | }); | ||
| 259 | |||
| 260 | r.cr1().modify(|w| { | ||
| 261 | // Enable interrupt for end of conversion | ||
| 262 | w.set_eocie(true); | ||
| 263 | // Enable interrupt for overrun | ||
| 264 | w.set_ovrie(true); | ||
| 265 | // Scanning converisons of multiple channels | ||
| 266 | w.set_scan(true); | ||
| 267 | // Continuous conversion mode | ||
| 268 | w.set_discen(false); | ||
| 269 | }); | 241 | }); |
| 270 | 242 | ||
| 271 | r.cr2().modify(|w| { | 243 | VrefInt {} |
| 272 | // Enable DMA mode | ||
| 273 | w.set_dma(true); | ||
| 274 | // Enable continuous conversions | ||
| 275 | w.set_cont(true); | ||
| 276 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 277 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 278 | // EOC flag is set at the end of each conversion. | ||
| 279 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 280 | }); | ||
| 281 | } | 244 | } |
| 282 | 245 | ||
| 283 | pub(super) fn teardown_dma() { | 246 | /// Enables internal temperature sensor and returns [Temperature], which can be used in |
| 284 | let r = T::regs(); | 247 | /// [Adc::read_internal()] to perform conversion. |
| 285 | 248 | /// | |
| 286 | // Stop ADC | 249 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, |
| 287 | r.cr2().modify(|reg| { | 250 | /// temperature sensor will return vbat value. |
| 288 | // Stop ADC | 251 | pub fn enable_temperature(&self) -> Temperature { |
| 289 | reg.set_swstart(false); | 252 | T::common_regs().ccr().modify(|reg| { |
| 290 | // Stop ADC | 253 | reg.set_tsvrefe(true); |
| 291 | reg.set_adon(false); | ||
| 292 | // Stop DMA | ||
| 293 | reg.set_dma(false); | ||
| 294 | }); | 254 | }); |
| 295 | 255 | ||
| 296 | r.cr1().modify(|w| { | 256 | Temperature {} |
| 297 | // Disable interrupt for end of conversion | 257 | } |
| 298 | w.set_eocie(false); | ||
| 299 | // Disable interrupt for overrun | ||
| 300 | w.set_ovrie(false); | ||
| 301 | }); | ||
| 302 | 258 | ||
| 303 | clear_interrupt_flags(r); | 259 | /// Enables vbat input and returns [Vbat], which can be used in |
| 260 | /// [Adc::read_internal()] to perform conversion. | ||
| 261 | pub fn enable_vbat(&self) -> Vbat { | ||
| 262 | T::common_regs().ccr().modify(|reg| { | ||
| 263 | reg.set_vbate(true); | ||
| 264 | }); | ||
| 304 | 265 | ||
| 305 | compiler_fence(Ordering::SeqCst); | 266 | Vbat {} |
| 306 | } | 267 | } |
| 307 | } | 268 | } |
| 308 | 269 | ||
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index cbc217545..62b5043ee 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | #[cfg(adc_g0)] | 2 | #[cfg(adc_g0)] |
| 3 | use heapless::Vec; | 3 | use heapless::Vec; |
| 4 | use pac::adc::vals::Dmacfg; | ||
| 5 | #[cfg(adc_g0)] | 4 | #[cfg(adc_g0)] |
| 6 | use pac::adc::vals::{Ckmode, Smpsel}; | 5 | use pac::adc::vals::Ckmode; |
| 6 | use pac::adc::vals::Dmacfg; | ||
| 7 | #[cfg(adc_v3)] | 7 | #[cfg(adc_v3)] |
| 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; |
| 9 | #[cfg(adc_g0)] | 9 | #[cfg(adc_g0)] |
| @@ -11,18 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | |||
| 11 | 11 | ||
| 12 | #[allow(unused_imports)] | 12 | #[allow(unused_imports)] |
| 13 | use super::SealedAdcChannel; | 13 | use super::SealedAdcChannel; |
| 14 | use super::{ | 14 | use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, | 15 | use crate::adc::ConversionMode; |
| 16 | blocking_delay_us, | ||
| 17 | }; | ||
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | mod ringbuffered; | ||
| 21 | |||
| 22 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 23 | use ringbuffered::RingBufferedAdc; | ||
| 24 | |||
| 25 | use crate::dma::Transfer; | ||
| 26 | use crate::{Peri, pac, rcc}; | 16 | use crate::{Peri, pac, rcc}; |
| 27 | 17 | ||
| 28 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| @@ -89,7 +79,7 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 89 | cfg_if! { | 79 | cfg_if! { |
| 90 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 91 | pub struct VddCore; | 81 | pub struct VddCore; |
| 92 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 93 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 94 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 95 | 6 | 85 | 6 |
| @@ -101,7 +91,7 @@ cfg_if! { | |||
| 101 | cfg_if! { | 91 | cfg_if! { |
| 102 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 103 | pub struct DacOut; | 93 | pub struct DacOut; |
| 104 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 105 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 106 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 107 | 19 | 97 | 19 |
| @@ -145,6 +135,32 @@ pub enum Clock { | |||
| 145 | 135 | ||
| 146 | }} | 136 | }} |
| 147 | 137 | ||
| 138 | #[cfg(adc_u0)] | ||
| 139 | type Ovss = u8; | ||
| 140 | #[cfg(adc_u0)] | ||
| 141 | type Ovsr = u8; | ||
| 142 | #[cfg(adc_v3)] | ||
| 143 | type Ovss = OversamplingShift; | ||
| 144 | #[cfg(adc_v3)] | ||
| 145 | type Ovsr = OversamplingRatio; | ||
| 146 | |||
| 147 | /// Adc configuration | ||
| 148 | #[derive(Default)] | ||
| 149 | pub struct AdcConfig { | ||
| 150 | #[cfg(any(adc_u0, adc_g0, adc_v3))] | ||
| 151 | pub oversampling_shift: Option<Ovss>, | ||
| 152 | #[cfg(any(adc_u0, adc_g0, adc_v3))] | ||
| 153 | pub oversampling_ratio: Option<Ovsr>, | ||
| 154 | #[cfg(any(adc_u0, adc_g0))] | ||
| 155 | pub oversampling_enable: Option<bool>, | ||
| 156 | #[cfg(adc_v3)] | ||
| 157 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 158 | #[cfg(adc_g0)] | ||
| 159 | pub clock: Option<Clock>, | ||
| 160 | pub resolution: Option<Resolution>, | ||
| 161 | pub averaging: Option<Averaging>, | ||
| 162 | } | ||
| 163 | |||
| 148 | impl<'d, T: Instance> Adc<'d, T> { | 164 | impl<'d, T: Instance> Adc<'d, T> { |
| 149 | /// Enable the voltage regulator | 165 | /// Enable the voltage regulator |
| 150 | fn init_regulator() { | 166 | fn init_regulator() { |
| @@ -178,38 +194,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 178 | blocking_delay_us(1); | 194 | blocking_delay_us(1); |
| 179 | } | 195 | } |
| 180 | 196 | ||
| 181 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 182 | pub(super) fn start() { | ||
| 183 | // Start adc conversion | ||
| 184 | T::regs().cr().modify(|reg| { | ||
| 185 | reg.set_adstart(true); | ||
| 186 | }); | ||
| 187 | } | ||
| 188 | |||
| 189 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 190 | pub(super) fn stop() { | ||
| 191 | // Stop adc conversion | ||
| 192 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 193 | T::regs().cr().modify(|reg| { | ||
| 194 | reg.set_adstp(true); | ||
| 195 | }); | ||
| 196 | while T::regs().cr().read().adstart() {} | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 201 | pub(super) fn teardown_dma() { | ||
| 202 | //disable dma control | ||
| 203 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 204 | T::regs().cfgr().modify(|reg| { | ||
| 205 | reg.set_dmaen(false); | ||
| 206 | }); | ||
| 207 | #[cfg(any(adc_g0, adc_u0))] | ||
| 208 | T::regs().cfgr1().modify(|reg| { | ||
| 209 | reg.set_dmaen(false); | ||
| 210 | }); | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Initialize the ADC leaving any analog clock at reset value. | 197 | /// Initialize the ADC leaving any analog clock at reset value. |
| 214 | /// For G0 and WL, this is the async clock without prescaler. | 198 | /// For G0 and WL, this is the async clock without prescaler. |
| 215 | pub fn new(adc: Peri<'d, T>) -> Self { | 199 | pub fn new(adc: Peri<'d, T>) -> Self { |
| @@ -218,6 +202,73 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 218 | Self { adc } | 202 | Self { adc } |
| 219 | } | 203 | } |
| 220 | 204 | ||
| 205 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 206 | #[cfg(not(adc_g0))] | ||
| 207 | let s = Self::new(adc); | ||
| 208 | |||
| 209 | #[cfg(adc_g0)] | ||
| 210 | let s = match config.clock { | ||
| 211 | Some(clock) => Self::new_with_clock(adc, clock), | ||
| 212 | None => Self::new(adc), | ||
| 213 | }; | ||
| 214 | |||
| 215 | #[cfg(any(adc_g0, adc_u0, adc_v3))] | ||
| 216 | if let Some(shift) = config.oversampling_shift { | ||
| 217 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 218 | } | ||
| 219 | |||
| 220 | #[cfg(any(adc_g0, adc_u0, adc_v3))] | ||
| 221 | if let Some(ratio) = config.oversampling_ratio { | ||
| 222 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 223 | } | ||
| 224 | |||
| 225 | #[cfg(any(adc_g0, adc_u0))] | ||
| 226 | if let Some(enable) = config.oversampling_enable { | ||
| 227 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | ||
| 228 | } | ||
| 229 | |||
| 230 | #[cfg(adc_v3)] | ||
| 231 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 232 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 233 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 234 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 235 | } | ||
| 236 | |||
| 237 | if let Some(resolution) = config.resolution { | ||
| 238 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 239 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 240 | #[cfg(any(adc_g0, adc_u0))] | ||
| 241 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 242 | } | ||
| 243 | |||
| 244 | if let Some(averaging) = config.averaging { | ||
| 245 | let (enable, samples, right_shift) = match averaging { | ||
| 246 | Averaging::Disabled => (false, 0, 0), | ||
| 247 | Averaging::Samples2 => (true, 0, 1), | ||
| 248 | Averaging::Samples4 => (true, 1, 2), | ||
| 249 | Averaging::Samples8 => (true, 2, 3), | ||
| 250 | Averaging::Samples16 => (true, 3, 4), | ||
| 251 | Averaging::Samples32 => (true, 4, 5), | ||
| 252 | Averaging::Samples64 => (true, 5, 6), | ||
| 253 | Averaging::Samples128 => (true, 6, 7), | ||
| 254 | Averaging::Samples256 => (true, 7, 8), | ||
| 255 | }; | ||
| 256 | T::regs().cfgr2().modify(|reg| { | ||
| 257 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 258 | reg.set_rovse(enable); | ||
| 259 | #[cfg(any(adc_g0, adc_u0))] | ||
| 260 | reg.set_ovse(enable); | ||
| 261 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 262 | reg.set_ovsr(samples.into()); | ||
| 263 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 264 | reg.set_ovsr(samples.into()); | ||
| 265 | reg.set_ovss(right_shift.into()); | ||
| 266 | }) | ||
| 267 | } | ||
| 268 | |||
| 269 | s | ||
| 270 | } | ||
| 271 | |||
| 221 | #[cfg(adc_g0)] | 272 | #[cfg(adc_g0)] |
| 222 | /// Initialize ADC with explicit clock for the analog ADC | 273 | /// Initialize ADC with explicit clock for the analog ADC |
| 223 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | 274 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| @@ -255,7 +306,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 255 | } | 306 | } |
| 256 | 307 | ||
| 257 | // Enable ADC only when it is not already running. | 308 | // Enable ADC only when it is not already running. |
| 258 | fn enable(&mut self) { | 309 | pub(super) fn enable() { |
| 259 | // Make sure bits are off | 310 | // Make sure bits are off |
| 260 | while T::regs().cr().read().addis() { | 311 | while T::regs().cr().read().addis() { |
| 261 | // spin | 312 | // spin |
| @@ -276,258 +327,75 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 276 | } | 327 | } |
| 277 | } | 328 | } |
| 278 | 329 | ||
| 279 | pub fn enable_vrefint(&self) -> VrefInt { | 330 | pub(super) fn start() { |
| 280 | #[cfg(not(any(adc_g0, adc_u0)))] | 331 | #[cfg(any(adc_v3, adc_g0, adc_u0))] |
| 281 | T::common_regs().ccr().modify(|reg| { | 332 | { |
| 282 | reg.set_vrefen(true); | 333 | // Start adc conversion |
| 283 | }); | 334 | T::regs().cr().modify(|reg| { |
| 284 | #[cfg(any(adc_g0, adc_u0))] | 335 | reg.set_adstart(true); |
| 285 | T::regs().ccr().modify(|reg| { | 336 | }); |
| 286 | reg.set_vrefen(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 290 | // to stabilize the internal voltage reference. | ||
| 291 | blocking_delay_us(15); | ||
| 292 | |||
| 293 | VrefInt {} | ||
| 294 | } | ||
| 295 | |||
| 296 | pub fn enable_temperature(&self) -> Temperature { | ||
| 297 | cfg_if! { | ||
| 298 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 299 | T::regs().ccr().modify(|reg| { | ||
| 300 | reg.set_tsen(true); | ||
| 301 | }); | ||
| 302 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 303 | T::common_regs().ccr().modify(|reg| { | ||
| 304 | reg.set_tsen(true); | ||
| 305 | }); | ||
| 306 | } else { | ||
| 307 | T::common_regs().ccr().modify(|reg| { | ||
| 308 | reg.set_ch17sel(true); | ||
| 309 | }); | ||
| 310 | } | ||
| 311 | } | 337 | } |
| 312 | |||
| 313 | Temperature {} | ||
| 314 | } | 338 | } |
| 315 | 339 | ||
| 316 | pub fn enable_vbat(&self) -> Vbat { | 340 | pub(super) fn stop() { |
| 317 | cfg_if! { | 341 | #[cfg(any(adc_v3, adc_g0, adc_u0))] |
| 318 | if #[cfg(any(adc_g0, adc_u0))] { | 342 | { |
| 319 | T::regs().ccr().modify(|reg| { | 343 | // Ensure conversions are finished. |
| 320 | reg.set_vbaten(true); | 344 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 321 | }); | 345 | T::regs().cr().modify(|reg| { |
| 322 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 346 | reg.set_adstp(true); |
| 323 | T::common_regs().ccr().modify(|reg| { | ||
| 324 | reg.set_vbaten(true); | ||
| 325 | }); | ||
| 326 | } else { | ||
| 327 | T::common_regs().ccr().modify(|reg| { | ||
| 328 | reg.set_ch18sel(true); | ||
| 329 | }); | 347 | }); |
| 348 | while T::regs().cr().read().adstart() {} | ||
| 330 | } | 349 | } |
| 331 | } | ||
| 332 | |||
| 333 | Vbat {} | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Set the ADC resolution. | ||
| 337 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 338 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 339 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 340 | #[cfg(any(adc_g0, adc_u0))] | ||
| 341 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 342 | } | ||
| 343 | 350 | ||
| 344 | pub fn set_averaging(&mut self, averaging: Averaging) { | 351 | // Reset configuration. |
| 345 | let (enable, samples, right_shift) = match averaging { | ||
| 346 | Averaging::Disabled => (false, 0, 0), | ||
| 347 | Averaging::Samples2 => (true, 0, 1), | ||
| 348 | Averaging::Samples4 => (true, 1, 2), | ||
| 349 | Averaging::Samples8 => (true, 2, 3), | ||
| 350 | Averaging::Samples16 => (true, 3, 4), | ||
| 351 | Averaging::Samples32 => (true, 4, 5), | ||
| 352 | Averaging::Samples64 => (true, 5, 6), | ||
| 353 | Averaging::Samples128 => (true, 6, 7), | ||
| 354 | Averaging::Samples256 => (true, 7, 8), | ||
| 355 | }; | ||
| 356 | T::regs().cfgr2().modify(|reg| { | ||
| 357 | #[cfg(not(any(adc_g0, adc_u0)))] | 352 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 358 | reg.set_rovse(enable); | 353 | T::regs().cfgr().modify(|reg| { |
| 354 | reg.set_cont(false); | ||
| 355 | reg.set_dmaen(false); | ||
| 356 | }); | ||
| 359 | #[cfg(any(adc_g0, adc_u0))] | 357 | #[cfg(any(adc_g0, adc_u0))] |
| 360 | reg.set_ovse(enable); | 358 | T::regs().cfgr1().modify(|reg| { |
| 361 | #[cfg(any(adc_h5, adc_h7rs))] | 359 | reg.set_cont(false); |
| 362 | reg.set_ovsr(samples.into()); | 360 | reg.set_dmaen(false); |
| 363 | #[cfg(not(any(adc_h5, adc_h7rs)))] | 361 | }); |
| 364 | reg.set_ovsr(samples.into()); | ||
| 365 | reg.set_ovss(right_shift.into()); | ||
| 366 | }) | ||
| 367 | } | ||
| 368 | /* | ||
| 369 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 370 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 371 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 372 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 373 | + 30.0 | ||
| 374 | } | ||
| 375 | */ | ||
| 376 | |||
| 377 | /// Perform a single conversion. | ||
| 378 | fn convert(&mut self) -> u16 { | ||
| 379 | T::regs().isr().modify(|reg| { | ||
| 380 | reg.set_eos(true); | ||
| 381 | reg.set_eoc(true); | ||
| 382 | }); | ||
| 383 | |||
| 384 | // Start conversion | ||
| 385 | T::regs().cr().modify(|reg| { | ||
| 386 | reg.set_adstart(true); | ||
| 387 | }); | ||
| 388 | |||
| 389 | while !T::regs().isr().read().eos() { | ||
| 390 | // spin | ||
| 391 | } | 362 | } |
| 392 | |||
| 393 | T::regs().dr().read().0 as u16 | ||
| 394 | } | 363 | } |
| 395 | 364 | ||
| 396 | /// Read an ADC channel. | 365 | /// Perform a single conversion. |
| 397 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 366 | pub(super) fn convert() -> u16 { |
| 398 | self.read_channel(channel, sample_time) | 367 | // Some models are affected by an erratum: |
| 399 | } | 368 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| 400 | 369 | // corrupted, so we discard it and measure again. | |
| 401 | /// Read one or multiple ADC channels using DMA. | 370 | // |
| 402 | /// | 371 | // STM32L471xx: Section 2.7.3 |
| 403 | /// `readings` must have a length that is a multiple of the length of the | 372 | // STM32G4: Section 2.7.3 |
| 404 | /// `sequence` iterator. | 373 | #[cfg(any(rcc_l4, rcc_g4))] |
| 405 | /// | 374 | let len = 2; |
| 406 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 407 | /// channel number and not the pin order in `sequence`. | ||
| 408 | /// | ||
| 409 | /// Example | ||
| 410 | /// ```rust,ignore | ||
| 411 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 412 | /// | ||
| 413 | /// let mut adc = Adc::new(p.ADC1); | ||
| 414 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 415 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 416 | /// let mut measurements = [0u16; 2]; | ||
| 417 | /// | ||
| 418 | /// adc.read( | ||
| 419 | /// p.DMA1_CH2.reborrow(), | ||
| 420 | /// [ | ||
| 421 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 422 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 423 | /// ] | ||
| 424 | /// .into_iter(), | ||
| 425 | /// &mut measurements, | ||
| 426 | /// ) | ||
| 427 | /// .await; | ||
| 428 | /// defmt::info!("measurements: {}", measurements); | ||
| 429 | /// ``` | ||
| 430 | pub async fn read( | ||
| 431 | &mut self, | ||
| 432 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 433 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 434 | readings: &mut [u16], | ||
| 435 | ) { | ||
| 436 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 437 | assert!( | ||
| 438 | readings.len() % sequence.len() == 0, | ||
| 439 | "Readings length must be a multiple of sequence length" | ||
| 440 | ); | ||
| 441 | assert!( | ||
| 442 | sequence.len() <= 16, | ||
| 443 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 444 | ); | ||
| 445 | |||
| 446 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 447 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 448 | |||
| 449 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 450 | Self::cancel_conversions(); | ||
| 451 | self.enable(); | ||
| 452 | 375 | ||
| 453 | // Set sequence length | 376 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 454 | #[cfg(not(any(adc_g0, adc_u0)))] | 377 | let len = 1; |
| 455 | T::regs().sqr1().modify(|w| { | ||
| 456 | w.set_l(sequence.len() as u8 - 1); | ||
| 457 | }); | ||
| 458 | 378 | ||
| 459 | #[cfg(adc_g0)] | 379 | for _ in 0..len { |
| 460 | { | 380 | T::regs().isr().modify(|reg| { |
| 461 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | 381 | reg.set_eos(true); |
| 462 | 382 | reg.set_eoc(true); | |
| 463 | T::regs().chselr().write(|chselr| { | ||
| 464 | T::regs().smpr().write(|smpr| { | ||
| 465 | for (channel, sample_time) in sequence { | ||
| 466 | chselr.set_chsel(channel.channel.into(), true); | ||
| 467 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 468 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 469 | } else { | ||
| 470 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 471 | if let Err(_) = sample_times.push(sample_time) { | ||
| 472 | panic!( | ||
| 473 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 474 | SAMPLE_TIMES_CAPACITY | ||
| 475 | ); | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | }) | ||
| 480 | }); | 383 | }); |
| 481 | } | ||
| 482 | #[cfg(not(adc_g0))] | ||
| 483 | { | ||
| 484 | #[cfg(adc_u0)] | ||
| 485 | let mut channel_mask = 0; | ||
| 486 | |||
| 487 | // Configure channels and ranks | ||
| 488 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 489 | Self::configure_channel(channel, sample_time); | ||
| 490 | 384 | ||
| 491 | // Each channel is sampled according to sequence | 385 | // Start conversion |
| 492 | #[cfg(not(any(adc_g0, adc_u0)))] | 386 | T::regs().cr().modify(|reg| { |
| 493 | match _i { | 387 | reg.set_adstart(true); |
| 494 | 0..=3 => { | 388 | }); |
| 495 | T::regs().sqr1().modify(|w| { | ||
| 496 | w.set_sq(_i, channel.channel()); | ||
| 497 | }); | ||
| 498 | } | ||
| 499 | 4..=8 => { | ||
| 500 | T::regs().sqr2().modify(|w| { | ||
| 501 | w.set_sq(_i - 4, channel.channel()); | ||
| 502 | }); | ||
| 503 | } | ||
| 504 | 9..=13 => { | ||
| 505 | T::regs().sqr3().modify(|w| { | ||
| 506 | w.set_sq(_i - 9, channel.channel()); | ||
| 507 | }); | ||
| 508 | } | ||
| 509 | 14..=15 => { | ||
| 510 | T::regs().sqr4().modify(|w| { | ||
| 511 | w.set_sq(_i - 14, channel.channel()); | ||
| 512 | }); | ||
| 513 | } | ||
| 514 | _ => unreachable!(), | ||
| 515 | } | ||
| 516 | 389 | ||
| 517 | #[cfg(adc_u0)] | 390 | while !T::regs().isr().read().eos() { |
| 518 | { | 391 | // spin |
| 519 | channel_mask |= 1 << channel.channel(); | ||
| 520 | } | ||
| 521 | } | 392 | } |
| 522 | |||
| 523 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | ||
| 524 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | ||
| 525 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | ||
| 526 | #[cfg(adc_u0)] | ||
| 527 | T::regs().chselr().modify(|reg| { | ||
| 528 | reg.set_chsel(channel_mask); | ||
| 529 | }); | ||
| 530 | } | 393 | } |
| 394 | |||
| 395 | T::regs().dr().read().0 as u16 | ||
| 396 | } | ||
| 397 | |||
| 398 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { | ||
| 531 | // Set continuous mode with oneshot dma. | 399 | // Set continuous mode with oneshot dma. |
| 532 | // Clear overrun flag before starting transfer. | 400 | // Clear overrun flag before starting transfer. |
| 533 | T::regs().isr().modify(|reg| { | 401 | T::regs().isr().modify(|reg| { |
| @@ -535,82 +403,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 535 | }); | 403 | }); |
| 536 | 404 | ||
| 537 | #[cfg(not(any(adc_g0, adc_u0)))] | 405 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 538 | T::regs().cfgr().modify(|reg| { | 406 | let regs = T::regs().cfgr(); |
| 539 | reg.set_discen(false); | 407 | |
| 540 | reg.set_cont(true); | ||
| 541 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 542 | reg.set_dmaen(true); | ||
| 543 | }); | ||
| 544 | #[cfg(any(adc_g0, adc_u0))] | 408 | #[cfg(any(adc_g0, adc_u0))] |
| 545 | T::regs().cfgr1().modify(|reg| { | 409 | let regs = T::regs().cfgr1(); |
| 410 | |||
| 411 | regs.modify(|reg| { | ||
| 546 | reg.set_discen(false); | 412 | reg.set_discen(false); |
| 547 | reg.set_cont(true); | 413 | reg.set_cont(true); |
| 548 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 414 | reg.set_dmacfg(match conversion_mode { |
| 415 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 416 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 417 | }); | ||
| 549 | reg.set_dmaen(true); | 418 | reg.set_dmaen(true); |
| 550 | }); | 419 | }); |
| 551 | |||
| 552 | let request = rx_dma.request(); | ||
| 553 | let transfer = unsafe { | ||
| 554 | Transfer::new_read( | ||
| 555 | rx_dma, | ||
| 556 | request, | ||
| 557 | T::regs().dr().as_ptr() as *mut u16, | ||
| 558 | readings, | ||
| 559 | Default::default(), | ||
| 560 | ) | ||
| 561 | }; | ||
| 562 | |||
| 563 | // Start conversion | ||
| 564 | T::regs().cr().modify(|reg| { | ||
| 565 | reg.set_adstart(true); | ||
| 566 | }); | ||
| 567 | |||
| 568 | // Wait for conversion sequence to finish. | ||
| 569 | transfer.await; | ||
| 570 | |||
| 571 | // Ensure conversions are finished. | ||
| 572 | Self::cancel_conversions(); | ||
| 573 | |||
| 574 | // Reset configuration. | ||
| 575 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 576 | T::regs().cfgr().modify(|reg| { | ||
| 577 | reg.set_cont(false); | ||
| 578 | }); | ||
| 579 | #[cfg(any(adc_g0, adc_u0))] | ||
| 580 | T::regs().cfgr1().modify(|reg| { | ||
| 581 | reg.set_cont(false); | ||
| 582 | }); | ||
| 583 | } | 420 | } |
| 584 | 421 | ||
| 585 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 422 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 586 | /// | ||
| 587 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 588 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 589 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 590 | /// | ||
| 591 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 592 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 593 | /// | ||
| 594 | /// [`read`]: #method.read | ||
| 595 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 596 | pub fn into_ring_buffered<'a>( | ||
| 597 | &mut self, | ||
| 598 | dma: Peri<'a, impl RxDma<T>>, | ||
| 599 | dma_buf: &'a mut [u16], | ||
| 600 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 601 | ) -> RingBufferedAdc<'a, T> { | ||
| 602 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 603 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 604 | assert!( | ||
| 605 | sequence.len() <= 16, | ||
| 606 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 607 | ); | ||
| 608 | // reset conversions and enable the adc | ||
| 609 | Self::cancel_conversions(); | ||
| 610 | self.enable(); | ||
| 611 | |||
| 612 | //adc side setup | ||
| 613 | |||
| 614 | // Set sequence length | 423 | // Set sequence length |
| 615 | #[cfg(not(any(adc_g0, adc_u0)))] | 424 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 616 | T::regs().sqr1().modify(|w| { | 425 | T::regs().sqr1().modify(|w| { |
| @@ -623,10 +432,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 623 | 432 | ||
| 624 | T::regs().chselr().write(|chselr| { | 433 | T::regs().chselr().write(|chselr| { |
| 625 | T::regs().smpr().write(|smpr| { | 434 | T::regs().smpr().write(|smpr| { |
| 626 | for (channel, sample_time) in sequence { | 435 | for ((channel, _), sample_time) in sequence { |
| 627 | chselr.set_chsel(channel.channel.into(), true); | 436 | chselr.set_chsel(channel.into(), true); |
| 628 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | 437 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 629 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 438 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 630 | } else { | 439 | } else { |
| 631 | smpr.set_sample_time(sample_times.len(), sample_time); | 440 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 632 | if let Err(_) = sample_times.push(sample_time) { | 441 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -646,30 +455,63 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 646 | let mut channel_mask = 0; | 455 | let mut channel_mask = 0; |
| 647 | 456 | ||
| 648 | // Configure channels and ranks | 457 | // Configure channels and ranks |
| 649 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | 458 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 650 | Self::configure_channel(&mut channel, sample_time); | 459 | // RM0492, RM0481, etc. |
| 460 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 461 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 462 | if channel == 0 { | ||
| 463 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 464 | } | ||
| 465 | |||
| 466 | // Configure channel | ||
| 467 | cfg_if! { | ||
| 468 | if #[cfg(adc_u0)] { | ||
| 469 | // On G0 and U6 all channels use the same sampling time. | ||
| 470 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||
| 471 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 472 | match channel { | ||
| 473 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 474 | _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 475 | } | ||
| 476 | } else { | ||
| 477 | let sample_time = sample_time.into(); | ||
| 478 | T::regs() | ||
| 479 | .smpr(channel as usize / 10) | ||
| 480 | .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | #[cfg(stm32h7)] | ||
| 485 | { | ||
| 486 | use crate::pac::adc::vals::Pcsel; | ||
| 487 | |||
| 488 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 489 | T::regs() | ||
| 490 | .pcsel() | ||
| 491 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 492 | } | ||
| 651 | 493 | ||
| 652 | // Each channel is sampled according to sequence | 494 | // Each channel is sampled according to sequence |
| 653 | #[cfg(not(any(adc_g0, adc_u0)))] | 495 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 654 | match _i { | 496 | match _i { |
| 655 | 0..=3 => { | 497 | 0..=3 => { |
| 656 | T::regs().sqr1().modify(|w| { | 498 | T::regs().sqr1().modify(|w| { |
| 657 | w.set_sq(_i, channel.channel()); | 499 | w.set_sq(_i, channel); |
| 658 | }); | 500 | }); |
| 659 | } | 501 | } |
| 660 | 4..=8 => { | 502 | 4..=8 => { |
| 661 | T::regs().sqr2().modify(|w| { | 503 | T::regs().sqr2().modify(|w| { |
| 662 | w.set_sq(_i - 4, channel.channel()); | 504 | w.set_sq(_i - 4, channel); |
| 663 | }); | 505 | }); |
| 664 | } | 506 | } |
| 665 | 9..=13 => { | 507 | 9..=13 => { |
| 666 | T::regs().sqr3().modify(|w| { | 508 | T::regs().sqr3().modify(|w| { |
| 667 | w.set_sq(_i - 9, channel.channel()); | 509 | w.set_sq(_i - 9, channel); |
| 668 | }); | 510 | }); |
| 669 | } | 511 | } |
| 670 | 14..=15 => { | 512 | 14..=15 => { |
| 671 | T::regs().sqr4().modify(|w| { | 513 | T::regs().sqr4().modify(|w| { |
| 672 | w.set_sq(_i - 14, channel.channel()); | 514 | w.set_sq(_i - 14, channel); |
| 673 | }); | 515 | }); |
| 674 | } | 516 | } |
| 675 | _ => unreachable!(), | 517 | _ => unreachable!(), |
| @@ -677,7 +519,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 677 | 519 | ||
| 678 | #[cfg(adc_u0)] | 520 | #[cfg(adc_u0)] |
| 679 | { | 521 | { |
| 680 | channel_mask |= 1 << channel.channel(); | 522 | channel_mask |= 1 << channel; |
| 681 | } | 523 | } |
| 682 | } | 524 | } |
| 683 | 525 | ||
| @@ -689,151 +531,71 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 689 | reg.set_chsel(channel_mask); | 531 | reg.set_chsel(channel_mask); |
| 690 | }); | 532 | }); |
| 691 | } | 533 | } |
| 692 | // Set continuous mode with Circular dma. | 534 | } |
| 693 | // Clear overrun flag before starting transfer. | ||
| 694 | T::regs().isr().modify(|reg| { | ||
| 695 | reg.set_ovr(true); | ||
| 696 | }); | ||
| 697 | 535 | ||
| 536 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 698 | #[cfg(not(any(adc_g0, adc_u0)))] | 537 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 699 | T::regs().cfgr().modify(|reg| { | 538 | T::common_regs().ccr().modify(|reg| { |
| 700 | reg.set_discen(false); | 539 | reg.set_vrefen(true); |
| 701 | reg.set_cont(true); | ||
| 702 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 703 | reg.set_dmaen(true); | ||
| 704 | }); | 540 | }); |
| 705 | #[cfg(any(adc_g0, adc_u0))] | 541 | #[cfg(any(adc_g0, adc_u0))] |
| 706 | T::regs().cfgr1().modify(|reg| { | 542 | T::regs().ccr().modify(|reg| { |
| 707 | reg.set_discen(false); | 543 | reg.set_vrefen(true); |
| 708 | reg.set_cont(true); | ||
| 709 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 710 | reg.set_dmaen(true); | ||
| 711 | }); | 544 | }); |
| 712 | 545 | ||
| 713 | RingBufferedAdc::new(dma, dma_buf) | 546 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us |
| 714 | } | 547 | // to stabilize the internal voltage reference. |
| 715 | 548 | blocking_delay_us(15); | |
| 716 | #[cfg(not(adc_g0))] | ||
| 717 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 718 | // RM0492, RM0481, etc. | ||
| 719 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 720 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 721 | if channel.channel() == 0 { | ||
| 722 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 723 | } | ||
| 724 | 549 | ||
| 725 | // Configure channel | 550 | VrefInt {} |
| 726 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 727 | } | 551 | } |
| 728 | 552 | ||
| 729 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 553 | pub fn enable_temperature(&self) -> Temperature { |
| 730 | self.enable(); | 554 | cfg_if! { |
| 731 | #[cfg(not(adc_g0))] | 555 | if #[cfg(any(adc_g0, adc_u0))] { |
| 732 | Self::configure_channel(channel, sample_time); | 556 | T::regs().ccr().modify(|reg| { |
| 733 | #[cfg(adc_g0)] | 557 | reg.set_tsen(true); |
| 734 | T::regs().smpr().write(|reg| { | 558 | }); |
| 735 | reg.set_sample_time(0, sample_time); | 559 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 736 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | 560 | T::common_regs().ccr().modify(|reg| { |
| 737 | }); | 561 | reg.set_tsen(true); |
| 738 | // Select channel | 562 | }); |
| 739 | #[cfg(not(any(adc_g0, adc_u0)))] | 563 | } else { |
| 740 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 564 | T::common_regs().ccr().modify(|reg| { |
| 741 | #[cfg(any(adc_g0, adc_u0))] | 565 | reg.set_ch17sel(true); |
| 742 | T::regs().chselr().write(|reg| { | 566 | }); |
| 743 | #[cfg(adc_g0)] | 567 | } |
| 744 | reg.set_chsel(channel.channel().into(), true); | ||
| 745 | #[cfg(adc_u0)] | ||
| 746 | reg.set_chsel(1 << channel.channel()); | ||
| 747 | }); | ||
| 748 | |||
| 749 | // Some models are affected by an erratum: | ||
| 750 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | ||
| 751 | // corrupted, so we discard it and measure again. | ||
| 752 | // | ||
| 753 | // STM32L471xx: Section 2.7.3 | ||
| 754 | // STM32G4: Section 2.7.3 | ||
| 755 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 756 | let _ = self.convert(); | ||
| 757 | let val = self.convert(); | ||
| 758 | |||
| 759 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 760 | |||
| 761 | // RM0492, RM0481, etc. | ||
| 762 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 763 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 764 | if channel.channel() == 0 { | ||
| 765 | T::regs().or().modify(|reg| reg.set_op0(false)); | ||
| 766 | } | 568 | } |
| 767 | 569 | ||
| 768 | val | 570 | Temperature {} |
| 769 | } | ||
| 770 | |||
| 771 | #[cfg(adc_g0)] | ||
| 772 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 773 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 774 | } | ||
| 775 | #[cfg(adc_u0)] | ||
| 776 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 777 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 778 | } | ||
| 779 | |||
| 780 | #[cfg(adc_g0)] | ||
| 781 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 782 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 783 | } | ||
| 784 | #[cfg(adc_u0)] | ||
| 785 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 786 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 787 | } | ||
| 788 | |||
| 789 | #[cfg(any(adc_g0, adc_u0))] | ||
| 790 | pub fn oversampling_enable(&mut self, enable: bool) { | ||
| 791 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | ||
| 792 | } | ||
| 793 | |||
| 794 | #[cfg(adc_v3)] | ||
| 795 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 796 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 797 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 798 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 799 | } | ||
| 800 | |||
| 801 | #[cfg(adc_v3)] | ||
| 802 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | ||
| 803 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 804 | } | ||
| 805 | |||
| 806 | #[cfg(adc_v3)] | ||
| 807 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | ||
| 808 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 809 | } | 571 | } |
| 810 | 572 | ||
| 811 | #[cfg(not(adc_g0))] | 573 | pub fn enable_vbat(&self) -> Vbat { |
| 812 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 813 | cfg_if! { | 574 | cfg_if! { |
| 814 | if #[cfg(adc_u0)] { | 575 | if #[cfg(any(adc_g0, adc_u0))] { |
| 815 | // On G0 and U6 all channels use the same sampling time. | 576 | T::regs().ccr().modify(|reg| { |
| 816 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 577 | reg.set_vbaten(true); |
| 578 | }); | ||
| 817 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 579 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 818 | match _ch { | 580 | T::common_regs().ccr().modify(|reg| { |
| 819 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 581 | reg.set_vbaten(true); |
| 820 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 582 | }); |
| 821 | } | ||
| 822 | } else { | 583 | } else { |
| 823 | let sample_time = sample_time.into(); | 584 | T::common_regs().ccr().modify(|reg| { |
| 824 | T::regs() | 585 | reg.set_ch18sel(true); |
| 825 | .smpr(_ch as usize / 10) | 586 | }); |
| 826 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 827 | } | 587 | } |
| 828 | } | 588 | } |
| 589 | |||
| 590 | Vbat {} | ||
| 829 | } | 591 | } |
| 830 | 592 | ||
| 831 | fn cancel_conversions() { | 593 | /* |
| 832 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 594 | /// Convert a raw sample from the `Temperature` to deg C |
| 833 | T::regs().cr().modify(|reg| { | 595 | pub fn to_degrees_centigrade(sample: u16) -> f32 { |
| 834 | reg.set_adstp(true); | 596 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) |
| 835 | }); | 597 | * (sample as f32 - VtempCal30::get().read() as f32) |
| 836 | while T::regs().cr().read().adstart() {} | 598 | + 30.0 |
| 837 | } | ||
| 838 | } | 599 | } |
| 600 | */ | ||
| 839 | } | 601 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1d5d3fb92..9be6bcd0b 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,11 +4,8 @@ use pac::adc::vals::{Adcaldif, Boost}; | |||
| 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; | 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; |
| 5 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 6 | 6 | ||
| 7 | use super::{ | 7 | use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 8 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, | 8 | use crate::adc::ConversionMode; |
| 9 | VrefInt, blocking_delay_us, | ||
| 10 | }; | ||
| 11 | use crate::dma::Transfer; | ||
| 12 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 13 | use crate::{Peri, pac, rcc}; | 10 | use crate::{Peri, pac, rcc}; |
| 14 | 11 | ||
| @@ -147,7 +144,48 @@ pub enum Averaging { | |||
| 147 | Samples1024, | 144 | Samples1024, |
| 148 | } | 145 | } |
| 149 | 146 | ||
| 147 | /// Adc configuration | ||
| 148 | #[derive(Default)] | ||
| 149 | pub struct AdcConfig { | ||
| 150 | pub resolution: Option<Resolution>, | ||
| 151 | pub averaging: Option<Averaging>, | ||
| 152 | } | ||
| 153 | |||
| 150 | impl<'d, T: Instance> Adc<'d, T> { | 154 | impl<'d, T: Instance> Adc<'d, T> { |
| 155 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 156 | let s = Self::new(adc); | ||
| 157 | |||
| 158 | // Set the ADC resolution. | ||
| 159 | if let Some(resolution) = config.resolution { | ||
| 160 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 161 | } | ||
| 162 | |||
| 163 | // Set hardware averaging. | ||
| 164 | if let Some(averaging) = config.averaging { | ||
| 165 | let (enable, samples, right_shift) = match averaging { | ||
| 166 | Averaging::Disabled => (false, 0, 0), | ||
| 167 | Averaging::Samples2 => (true, 1, 1), | ||
| 168 | Averaging::Samples4 => (true, 3, 2), | ||
| 169 | Averaging::Samples8 => (true, 7, 3), | ||
| 170 | Averaging::Samples16 => (true, 15, 4), | ||
| 171 | Averaging::Samples32 => (true, 31, 5), | ||
| 172 | Averaging::Samples64 => (true, 63, 6), | ||
| 173 | Averaging::Samples128 => (true, 127, 7), | ||
| 174 | Averaging::Samples256 => (true, 255, 8), | ||
| 175 | Averaging::Samples512 => (true, 511, 9), | ||
| 176 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 177 | }; | ||
| 178 | |||
| 179 | T::regs().cfgr2().modify(|reg| { | ||
| 180 | reg.set_rovse(enable); | ||
| 181 | reg.set_ovsr(samples); | ||
| 182 | reg.set_ovss(right_shift); | ||
| 183 | }) | ||
| 184 | } | ||
| 185 | |||
| 186 | s | ||
| 187 | } | ||
| 188 | |||
| 151 | /// Create a new ADC driver. | 189 | /// Create a new ADC driver. |
| 152 | pub fn new(adc: Peri<'d, T>) -> Self { | 190 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 153 | rcc::enable_and_reset::<T>(); | 191 | rcc::enable_and_reset::<T>(); |
| @@ -179,37 +217,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 179 | }; | 217 | }; |
| 180 | T::regs().cr().modify(|w| w.set_boost(boost)); | 218 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 181 | } | 219 | } |
| 182 | let mut s = Self { adc }; | ||
| 183 | s.power_up(); | ||
| 184 | s.configure_differential_inputs(); | ||
| 185 | |||
| 186 | s.calibrate(); | ||
| 187 | blocking_delay_us(1); | ||
| 188 | |||
| 189 | s.enable(); | ||
| 190 | s.configure(); | ||
| 191 | |||
| 192 | s | ||
| 193 | } | ||
| 194 | 220 | ||
| 195 | fn power_up(&mut self) { | ||
| 196 | T::regs().cr().modify(|reg| { | 221 | T::regs().cr().modify(|reg| { |
| 197 | reg.set_deeppwd(false); | 222 | reg.set_deeppwd(false); |
| 198 | reg.set_advregen(true); | 223 | reg.set_advregen(true); |
| 199 | }); | 224 | }); |
| 200 | 225 | ||
| 201 | blocking_delay_us(10); | 226 | blocking_delay_us(10); |
| 202 | } | ||
| 203 | 227 | ||
| 204 | fn configure_differential_inputs(&mut self) { | ||
| 205 | T::regs().difsel().modify(|w| { | 228 | T::regs().difsel().modify(|w| { |
| 206 | for n in 0..20 { | 229 | for n in 0..20 { |
| 207 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 230 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 208 | } | 231 | } |
| 209 | }); | 232 | }); |
| 210 | } | ||
| 211 | 233 | ||
| 212 | fn calibrate(&mut self) { | ||
| 213 | T::regs().cr().modify(|w| { | 234 | T::regs().cr().modify(|w| { |
| 214 | #[cfg(not(adc_u5))] | 235 | #[cfg(not(adc_u5))] |
| 215 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 236 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -219,80 +240,50 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 219 | T::regs().cr().modify(|w| w.set_adcal(true)); | 240 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 220 | 241 | ||
| 221 | while T::regs().cr().read().adcal() {} | 242 | while T::regs().cr().read().adcal() {} |
| 222 | } | ||
| 223 | 243 | ||
| 224 | fn enable(&mut self) { | 244 | blocking_delay_us(1); |
| 225 | T::regs().isr().write(|w| w.set_adrdy(true)); | 245 | |
| 226 | T::regs().cr().modify(|w| w.set_aden(true)); | 246 | Self::enable(); |
| 227 | while !T::regs().isr().read().adrdy() {} | ||
| 228 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 229 | } | ||
| 230 | 247 | ||
| 231 | fn configure(&mut self) { | ||
| 232 | // single conversion mode, software trigger | 248 | // single conversion mode, software trigger |
| 233 | T::regs().cfgr().modify(|w| { | 249 | T::regs().cfgr().modify(|w| { |
| 234 | w.set_cont(false); | 250 | w.set_cont(false); |
| 235 | w.set_exten(Exten::DISABLED); | 251 | w.set_exten(Exten::DISABLED); |
| 236 | }); | 252 | }); |
| 237 | } | ||
| 238 | 253 | ||
| 239 | /// Enable reading the voltage reference internal channel. | 254 | Self { adc } |
| 240 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 241 | T::common_regs().ccr().modify(|reg| { | ||
| 242 | reg.set_vrefen(true); | ||
| 243 | }); | ||
| 244 | |||
| 245 | VrefInt {} | ||
| 246 | } | 255 | } |
| 247 | 256 | ||
| 248 | /// Enable reading the temperature internal channel. | 257 | pub(super) fn enable() { |
| 249 | pub fn enable_temperature(&self) -> Temperature { | 258 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 250 | T::common_regs().ccr().modify(|reg| { | 259 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 251 | reg.set_vsenseen(true); | 260 | while !T::regs().isr().read().adrdy() {} |
| 252 | }); | 261 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 253 | |||
| 254 | Temperature {} | ||
| 255 | } | 262 | } |
| 256 | 263 | ||
| 257 | /// Enable reading the vbat internal channel. | 264 | pub(super) fn start() { |
| 258 | pub fn enable_vbat(&self) -> Vbat { | 265 | // Start conversion |
| 259 | T::common_regs().ccr().modify(|reg| { | 266 | T::regs().cr().modify(|reg| { |
| 260 | reg.set_vbaten(true); | 267 | reg.set_adstart(true); |
| 261 | }); | 268 | }); |
| 262 | |||
| 263 | Vbat {} | ||
| 264 | } | 269 | } |
| 265 | 270 | ||
| 266 | /// Set the ADC resolution. | 271 | pub(super) fn stop() { |
| 267 | pub fn set_resolution(&mut self, resolution: Resolution) { | 272 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 268 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 273 | T::regs().cr().modify(|reg| { |
| 269 | } | 274 | reg.set_adstp(Adstp::STOP); |
| 275 | }); | ||
| 276 | while T::regs().cr().read().adstart() {} | ||
| 277 | } | ||
| 270 | 278 | ||
| 271 | /// Set hardware averaging. | 279 | // Reset configuration. |
| 272 | pub fn set_averaging(&mut self, averaging: Averaging) { | 280 | T::regs().cfgr().modify(|reg| { |
| 273 | let (enable, samples, right_shift) = match averaging { | 281 | reg.set_cont(false); |
| 274 | Averaging::Disabled => (false, 0, 0), | 282 | reg.set_dmngt(Dmngt::from_bits(0)); |
| 275 | Averaging::Samples2 => (true, 1, 1), | 283 | }); |
| 276 | Averaging::Samples4 => (true, 3, 2), | ||
| 277 | Averaging::Samples8 => (true, 7, 3), | ||
| 278 | Averaging::Samples16 => (true, 15, 4), | ||
| 279 | Averaging::Samples32 => (true, 31, 5), | ||
| 280 | Averaging::Samples64 => (true, 63, 6), | ||
| 281 | Averaging::Samples128 => (true, 127, 7), | ||
| 282 | Averaging::Samples256 => (true, 255, 8), | ||
| 283 | Averaging::Samples512 => (true, 511, 9), | ||
| 284 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 285 | }; | ||
| 286 | |||
| 287 | T::regs().cfgr2().modify(|reg| { | ||
| 288 | reg.set_rovse(enable); | ||
| 289 | reg.set_ovsr(samples); | ||
| 290 | reg.set_ovss(right_shift); | ||
| 291 | }) | ||
| 292 | } | 284 | } |
| 293 | 285 | ||
| 294 | /// Perform a single conversion. | 286 | pub(super) fn convert() -> u16 { |
| 295 | fn convert(&mut self) -> u16 { | ||
| 296 | T::regs().isr().modify(|reg| { | 287 | T::regs().isr().modify(|reg| { |
| 297 | reg.set_eos(true); | 288 | reg.set_eos(true); |
| 298 | reg.set_eoc(true); | 289 | reg.set_eoc(true); |
| @@ -310,170 +301,96 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 310 | T::regs().dr().read().0 as u16 | 301 | T::regs().dr().read().0 as u16 |
| 311 | } | 302 | } |
| 312 | 303 | ||
| 313 | /// Read an ADC channel. | 304 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { |
| 314 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 305 | match conversion_mode { |
| 315 | self.read_channel(channel, sample_time) | 306 | ConversionMode::Singular => { |
| 307 | T::regs().isr().modify(|reg| { | ||
| 308 | reg.set_ovr(true); | ||
| 309 | }); | ||
| 310 | T::regs().cfgr().modify(|reg| { | ||
| 311 | reg.set_cont(true); | ||
| 312 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 313 | }); | ||
| 314 | } | ||
| 315 | _ => unreachable!(), | ||
| 316 | } | ||
| 316 | } | 317 | } |
| 317 | 318 | ||
| 318 | /// Read one or multiple ADC channels using DMA. | 319 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 319 | /// | ||
| 320 | /// `sequence` iterator and `readings` must have the same length. | ||
| 321 | /// | ||
| 322 | /// Example | ||
| 323 | /// ```rust,ignore | ||
| 324 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 325 | /// | ||
| 326 | /// let mut adc = Adc::new(p.ADC1); | ||
| 327 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 328 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 329 | /// let mut measurements = [0u16; 2]; | ||
| 330 | /// | ||
| 331 | /// adc.read( | ||
| 332 | /// p.DMA2_CH0.reborrow(), | ||
| 333 | /// [ | ||
| 334 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 335 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 336 | /// ] | ||
| 337 | /// .into_iter(), | ||
| 338 | /// &mut measurements, | ||
| 339 | /// ) | ||
| 340 | /// .await; | ||
| 341 | /// defmt::info!("measurements: {}", measurements); | ||
| 342 | /// ``` | ||
| 343 | pub async fn read( | ||
| 344 | &mut self, | ||
| 345 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 346 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 347 | readings: &mut [u16], | ||
| 348 | ) { | ||
| 349 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 350 | assert!( | ||
| 351 | sequence.len() == readings.len(), | ||
| 352 | "Sequence length must be equal to readings length" | ||
| 353 | ); | ||
| 354 | assert!( | ||
| 355 | sequence.len() <= 16, | ||
| 356 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 357 | ); | ||
| 358 | |||
| 359 | // Ensure no conversions are ongoing | ||
| 360 | Self::cancel_conversions(); | ||
| 361 | |||
| 362 | // Set sequence length | 320 | // Set sequence length |
| 363 | T::regs().sqr1().modify(|w| { | 321 | T::regs().sqr1().modify(|w| { |
| 364 | w.set_l(sequence.len() as u8 - 1); | 322 | w.set_l(sequence.len() as u8 - 1); |
| 365 | }); | 323 | }); |
| 366 | 324 | ||
| 367 | // Configure channels and ranks | 325 | // Configure channels and ranks |
| 368 | for (i, (channel, sample_time)) in sequence.enumerate() { | 326 | for (i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 369 | Self::configure_channel(channel, sample_time); | 327 | let sample_time = sample_time.into(); |
| 328 | if channel <= 9 { | ||
| 329 | T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); | ||
| 330 | } else { | ||
| 331 | T::regs() | ||
| 332 | .smpr(1) | ||
| 333 | .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); | ||
| 334 | } | ||
| 335 | |||
| 336 | #[cfg(any(stm32h7, stm32u5))] | ||
| 337 | { | ||
| 338 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 339 | T::regs() | ||
| 340 | .pcsel() | ||
| 341 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 342 | } | ||
| 343 | |||
| 370 | match i { | 344 | match i { |
| 371 | 0..=3 => { | 345 | 0..=3 => { |
| 372 | T::regs().sqr1().modify(|w| { | 346 | T::regs().sqr1().modify(|w| { |
| 373 | w.set_sq(i, channel.channel()); | 347 | w.set_sq(i, channel); |
| 374 | }); | 348 | }); |
| 375 | } | 349 | } |
| 376 | 4..=8 => { | 350 | 4..=8 => { |
| 377 | T::regs().sqr2().modify(|w| { | 351 | T::regs().sqr2().modify(|w| { |
| 378 | w.set_sq(i - 4, channel.channel()); | 352 | w.set_sq(i - 4, channel); |
| 379 | }); | 353 | }); |
| 380 | } | 354 | } |
| 381 | 9..=13 => { | 355 | 9..=13 => { |
| 382 | T::regs().sqr3().modify(|w| { | 356 | T::regs().sqr3().modify(|w| { |
| 383 | w.set_sq(i - 9, channel.channel()); | 357 | w.set_sq(i - 9, channel); |
| 384 | }); | 358 | }); |
| 385 | } | 359 | } |
| 386 | 14..=15 => { | 360 | 14..=15 => { |
| 387 | T::regs().sqr4().modify(|w| { | 361 | T::regs().sqr4().modify(|w| { |
| 388 | w.set_sq(i - 14, channel.channel()); | 362 | w.set_sq(i - 14, channel); |
| 389 | }); | 363 | }); |
| 390 | } | 364 | } |
| 391 | _ => unreachable!(), | 365 | _ => unreachable!(), |
| 392 | } | 366 | } |
| 393 | } | 367 | } |
| 394 | |||
| 395 | // Set continuous mode with oneshot dma. | ||
| 396 | // Clear overrun flag before starting transfer. | ||
| 397 | |||
| 398 | T::regs().isr().modify(|reg| { | ||
| 399 | reg.set_ovr(true); | ||
| 400 | }); | ||
| 401 | T::regs().cfgr().modify(|reg| { | ||
| 402 | reg.set_cont(true); | ||
| 403 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 404 | }); | ||
| 405 | |||
| 406 | let request = rx_dma.request(); | ||
| 407 | let transfer = unsafe { | ||
| 408 | Transfer::new_read( | ||
| 409 | rx_dma, | ||
| 410 | request, | ||
| 411 | T::regs().dr().as_ptr() as *mut u16, | ||
| 412 | readings, | ||
| 413 | Default::default(), | ||
| 414 | ) | ||
| 415 | }; | ||
| 416 | |||
| 417 | // Start conversion | ||
| 418 | T::regs().cr().modify(|reg| { | ||
| 419 | reg.set_adstart(true); | ||
| 420 | }); | ||
| 421 | |||
| 422 | // Wait for conversion sequence to finish. | ||
| 423 | transfer.await; | ||
| 424 | |||
| 425 | // Ensure conversions are finished. | ||
| 426 | Self::cancel_conversions(); | ||
| 427 | |||
| 428 | // Reset configuration. | ||
| 429 | T::regs().cfgr().modify(|reg| { | ||
| 430 | reg.set_cont(false); | ||
| 431 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 432 | }); | ||
| 433 | } | 368 | } |
| 434 | 369 | ||
| 435 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 370 | /// Enable reading the voltage reference internal channel. |
| 436 | channel.setup(); | 371 | pub fn enable_vrefint(&self) -> VrefInt { |
| 437 | 372 | T::common_regs().ccr().modify(|reg| { | |
| 438 | let channel = channel.channel(); | 373 | reg.set_vrefen(true); |
| 439 | 374 | }); | |
| 440 | Self::set_channel_sample_time(channel, sample_time); | ||
| 441 | 375 | ||
| 442 | #[cfg(any(stm32h7, stm32u5))] | 376 | VrefInt {} |
| 443 | { | ||
| 444 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 445 | T::regs() | ||
| 446 | .pcsel() | ||
| 447 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 448 | } | ||
| 449 | } | 377 | } |
| 450 | 378 | ||
| 451 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 379 | /// Enable reading the temperature internal channel. |
| 452 | Self::configure_channel(channel, sample_time); | 380 | pub fn enable_temperature(&self) -> Temperature { |
| 453 | 381 | T::common_regs().ccr().modify(|reg| { | |
| 454 | T::regs().sqr1().modify(|reg| { | 382 | reg.set_vsenseen(true); |
| 455 | reg.set_sq(0, channel.channel()); | ||
| 456 | reg.set_l(0); | ||
| 457 | }); | 383 | }); |
| 458 | 384 | ||
| 459 | self.convert() | 385 | Temperature {} |
| 460 | } | 386 | } |
| 461 | 387 | ||
| 462 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 388 | /// Enable reading the vbat internal channel. |
| 463 | let sample_time = sample_time.into(); | 389 | pub fn enable_vbat(&self) -> Vbat { |
| 464 | if ch <= 9 { | 390 | T::common_regs().ccr().modify(|reg| { |
| 465 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | 391 | reg.set_vbaten(true); |
| 466 | } else { | 392 | }); |
| 467 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | 393 | ||
| 471 | fn cancel_conversions() { | 394 | Vbat {} |
| 472 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 473 | T::regs().cr().modify(|reg| { | ||
| 474 | reg.set_adstp(Adstp::STOP); | ||
| 475 | }); | ||
| 476 | while T::regs().cr().read().adstart() {} | ||
| 477 | } | ||
| 478 | } | 395 | } |
| 479 | } | 396 | } |
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5628cb827..694e85657 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | info!("Hello World!"); | 14 | info!("Hello World!"); |
| 15 | 15 | ||
| 16 | let mut delay = Delay; | 16 | let mut delay = Delay; |
| 17 | let mut adc = Adc::new(p.ADC1); | 17 | let mut adc = Adc::new_with_config(p.ADC1, Default::default()); |
| 18 | let mut pin = p.PC1; | 18 | let mut pin = p.PC1; |
| 19 | 19 | ||
| 20 | let mut vrefint = adc.enable_vrefint(); | 20 | let mut vrefint = adc.enable_vrefint(); |
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 01b881c79..d61b1b2eb 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs | |||
| @@ -4,7 +4,7 @@ use cortex_m::singleton; | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Peripherals; | 6 | use embassy_stm32::Peripherals; |
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, RingBufferedAdc, SampleTime}; |
| 8 | use embassy_time::Instant; | 8 | use embassy_time::Instant; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| @@ -20,8 +20,8 @@ async fn adc_task(p: Peripherals) { | |||
| 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); |
| 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); |
| 22 | 22 | ||
| 23 | let adc = Adc::new(p.ADC1); | 23 | let adc = Adc::new_with_config(p.ADC1, Default::default()); |
| 24 | let adc2 = Adc::new(p.ADC2); | 24 | let adc2 = Adc::new_with_config(p.ADC2, Default::default()); |
| 25 | 25 | ||
| 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( |
| 27 | p.DMA2_CH0, | 27 | p.DMA2_CH0, |
| @@ -31,6 +31,7 @@ async fn adc_task(p: Peripherals) { | |||
| 31 | (p.PA2.degrade_adc(), SampleTime::CYCLES112), | 31 | (p.PA2.degrade_adc(), SampleTime::CYCLES112), |
| 32 | ] | 32 | ] |
| 33 | .into_iter(), | 33 | .into_iter(), |
| 34 | RegularConversionMode::Continuous, | ||
| 34 | ); | 35 | ); |
| 35 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered( | 36 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered( |
| 36 | p.DMA2_CH2, | 37 | p.DMA2_CH2, |
| @@ -40,6 +41,7 @@ async fn adc_task(p: Peripherals) { | |||
| 40 | (p.PA3.degrade_adc(), SampleTime::CYCLES112), | 41 | (p.PA3.degrade_adc(), SampleTime::CYCLES112), |
| 41 | ] | 42 | ] |
| 42 | .into_iter(), | 43 | .into_iter(), |
| 44 | RegularConversionMode::Continuous, | ||
| 43 | ); | 45 | ); |
| 44 | 46 | ||
| 45 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around | 47 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around |
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index f6979889d..aa8b1771b 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, Clock, Ovsr, Ovss, Presc, SampleTime}; | 10 | use embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, 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,12 +16,14 @@ 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_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); | 19 | let mut config = AdcConfig::default(); |
| 20 | let mut pin = p.PA1; | 20 | config.clock = Some(Clock::Async { div: Presc::DIV1 }); |
| 21 | config.oversampling_ratio = Some(Ovsr::MUL16); | ||
| 22 | config.oversampling_shift = Some(Ovss::NO_SHIFT); | ||
| 23 | config.oversampling_enable = Some(true); | ||
| 21 | 24 | ||
| 22 | adc.set_oversampling_ratio(Ovsr::MUL16); | 25 | let mut adc = Adc::new_with_config(p.ADC1, config); |
| 23 | adc.set_oversampling_shift(Ovss::NO_SHIFT); | 26 | let mut pin = p.PA1; |
| 24 | adc.oversampling_enable(true); | ||
| 25 | 27 | ||
| 26 | loop { | 28 | loop { |
| 27 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); | 29 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); |
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 94315141c..2149e0748 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -28,9 +28,9 @@ async fn main(_spawner: Spawner) { | |||
| 28 | let mut p = embassy_stm32::init(config); | 28 | let mut p = embassy_stm32::init(config); |
| 29 | info!("Hello World!"); | 29 | info!("Hello World!"); |
| 30 | 30 | ||
| 31 | let mut adc = Adc::new(p.ADC2); | 31 | let mut adc = Adc::new(p.ADC2, Default::default()); |
| 32 | 32 | ||
| 33 | let mut adc_temp = Adc::new(p.ADC1); | 33 | let mut adc_temp = Adc::new(p.ADC1, Default::default()); |
| 34 | let mut temperature = adc_temp.enable_temperature(); | 34 | let mut temperature = adc_temp.enable_temperature(); |
| 35 | 35 | ||
| 36 | loop { | 36 | loop { |
diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index 2773723e9..6dedf88d6 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs | |||
| @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 32 | } | 32 | } |
| 33 | let p = embassy_stm32::init(config); | 33 | let p = embassy_stm32::init(config); |
| 34 | 34 | ||
| 35 | let mut adc = Adc::new(p.ADC1); | 35 | let mut adc = Adc::new(p.ADC1, Default::default()); |
| 36 | let mut differential_channel = (p.PA0, p.PA1); | 36 | let mut differential_channel = (p.PA0, p.PA1); |
| 37 | 37 | ||
| 38 | // can also use | 38 | // can also use |
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs index ef8b0c3c2..478b6b2ca 100644 --- a/examples/stm32g4/src/bin/adc_dma.rs +++ b/examples/stm32g4/src/bin/adc_dma.rs | |||
| @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | 33 | ||
| 34 | info!("Hello World!"); | 34 | info!("Hello World!"); |
| 35 | 35 | ||
| 36 | let mut adc = Adc::new(p.ADC1); | 36 | let mut adc = Adc::new(p.ADC1, Default::default()); |
| 37 | 37 | ||
| 38 | let mut dma = p.DMA1_CH1; | 38 | let mut dma = p.DMA1_CH1; |
| 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); |
diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index 3ae2ff064..1e97fa925 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs | |||
| @@ -77,7 +77,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 77 | pwm.set_mms2(Mms2::UPDATE); | 77 | pwm.set_mms2(Mms2::UPDATE); |
| 78 | 78 | ||
| 79 | // Configure regular conversions with DMA | 79 | // Configure regular conversions with DMA |
| 80 | let adc1 = Adc::new(p.ADC1); | 80 | let adc1 = Adc::new(p.ADC1, Default::default()); |
| 81 | 81 | ||
| 82 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); | 82 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); |
| 83 | let pa0 = p.PC1.degrade_adc(); | 83 | let pa0 = p.PC1.degrade_adc(); |
diff --git a/examples/stm32g4/src/bin/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs index cb99ab2a7..87ffea4be 100644 --- a/examples/stm32g4/src/bin/adc_oversampling.rs +++ b/examples/stm32g4/src/bin/adc_oversampling.rs | |||
| @@ -9,7 +9,7 @@ use defmt::*; | |||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::Config; | 10 | use embassy_stm32::Config; |
| 11 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; | 11 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; |
| 12 | use embassy_stm32::adc::{Adc, SampleTime}; | 12 | use embassy_stm32::adc::{Adc, AdcConfig, SampleTime}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 15 | ||
| @@ -32,7 +32,8 @@ async fn main(_spawner: Spawner) { | |||
| 32 | } | 32 | } |
| 33 | let mut p = embassy_stm32::init(config); | 33 | let mut p = embassy_stm32::init(config); |
| 34 | 34 | ||
| 35 | let mut adc = Adc::new(p.ADC1); | 35 | let mut config = AdcConfig::default(); |
| 36 | |||
| 36 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | 37 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf |
| 37 | // page652 Oversampler | 38 | // page652 Oversampler |
| 38 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation | 39 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation |
| @@ -44,9 +45,11 @@ async fn main(_spawner: Spawner) { | |||
| 44 | // 0x05 oversampling ratio X64 | 45 | // 0x05 oversampling ratio X64 |
| 45 | // 0x06 oversampling ratio X128 | 46 | // 0x06 oversampling ratio X128 |
| 46 | // 0x07 oversampling ratio X256 | 47 | // 0x07 oversampling ratio X256 |
| 47 | adc.set_oversampling_ratio(0x03); // ratio X3 | 48 | config.oversampling_ratio = Some(0x03); // ratio X3 |
| 48 | adc.set_oversampling_shift(0b0000); // no shift | 49 | config.oversampling_shift = Some(0b0000); // no shift |
| 49 | adc.enable_regular_oversampling_mode(Rovsm::RESUMED, Trovs::AUTOMATIC, true); | 50 | config.oversampling_mode = Some((Rovsm::RESUMED, Trovs::AUTOMATIC, true)); |
| 51 | |||
| 52 | let mut adc = Adc::new(p.ADC1, config); | ||
| 50 | 53 | ||
| 51 | loop { | 54 | loop { |
| 52 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); | 55 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); |
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 835bf5411..42766a5e3 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::Config; | 5 | use embassy_stm32::Config; |
| 6 | use embassy_stm32::adc::{Adc, Resolution, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; |
| 7 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 8 | ||
| 9 | #[cortex_m_rt::entry] | 9 | #[cortex_m_rt::entry] |
| @@ -17,9 +17,12 @@ fn main() -> ! { | |||
| 17 | } | 17 | } |
| 18 | let p = embassy_stm32::init(config); | 18 | let p = embassy_stm32::init(config); |
| 19 | 19 | ||
| 20 | let mut adc = Adc::new(p.ADC1); | 20 | let mut config = AdcConfig::default(); |
| 21 | config.resolution = Some(Resolution::BITS8); | ||
| 22 | |||
| 23 | let mut adc = Adc::new_with_config(p.ADC1, config); | ||
| 21 | //adc.enable_vref(); | 24 | //adc.enable_vref(); |
| 22 | adc.set_resolution(Resolution::BITS8); | 25 | |
| 23 | let mut channel = p.PC0; | 26 | let mut channel = p.PC0; |
| 24 | 27 | ||
| 25 | loop { | 28 | loop { |
diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index ab1e9d2e9..550da95a4 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Config; | 6 | use embassy_stm32::Config; |
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, SampleTime}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | const DMA_BUF_LEN: usize = 512; | 10 | const DMA_BUF_LEN: usize = 512; |
| @@ -20,7 +20,7 @@ async fn main(_spawner: Spawner) { | |||
| 20 | } | 20 | } |
| 21 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 22 | 22 | ||
| 23 | let mut adc = Adc::new(p.ADC1); | 23 | let adc = Adc::new(p.ADC1); |
| 24 | let adc_pin0 = p.PA0.degrade_adc(); | 24 | let adc_pin0 = p.PA0.degrade_adc(); |
| 25 | let adc_pin1 = p.PA1.degrade_adc(); | 25 | let adc_pin1 = p.PA1.degrade_adc(); |
| 26 | let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; | 26 | let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; |
| @@ -29,6 +29,7 @@ async fn main(_spawner: Spawner) { | |||
| 29 | p.DMA1_CH1, | 29 | p.DMA1_CH1, |
| 30 | &mut adc_dma_buf, | 30 | &mut adc_dma_buf, |
| 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), | 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), |
| 32 | RegularConversionMode::Continuous, | ||
| 32 | ); | 33 | ); |
| 33 | 34 | ||
| 34 | info!("starting measurement loop"); | 35 | info!("starting measurement loop"); |
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 4fbc6f17f..53bd37303 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::Config; | 5 | use embassy_stm32::Config; |
| 6 | use embassy_stm32::adc::{Adc, Resolution, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; |
| 7 | use embassy_time::Duration; | 7 | use embassy_time::Duration; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -18,8 +18,9 @@ fn main() -> ! { | |||
| 18 | } | 18 | } |
| 19 | let p = embassy_stm32::init(config); | 19 | let p = embassy_stm32::init(config); |
| 20 | 20 | ||
| 21 | let mut adc = Adc::new(p.ADC1); | 21 | let mut config = AdcConfig::default(); |
| 22 | adc.set_resolution(Resolution::BITS8); | 22 | config.resolution = Some(Resolution::BITS8); |
| 23 | let mut adc = Adc::new_with_config(p.ADC1, config); | ||
| 23 | let mut channel = p.PC0; | 24 | let mut channel = p.PC0; |
| 24 | 25 | ||
| 25 | loop { | 26 | loop { |
diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 99944f7c7..6b9a91d6e 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::adc::{self, AdcChannel, SampleTime, adc4}; | 5 | use embassy_stm32::adc::{self, AdcChannel, AdcConfig, SampleTime, adc4}; |
| 6 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 7 | 7 | ||
| 8 | #[embassy_executor::main] | 8 | #[embassy_executor::main] |
| @@ -12,19 +12,21 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 12 | let mut p = embassy_stm32::init(config); | 12 | let mut p = embassy_stm32::init(config); |
| 13 | 13 | ||
| 14 | // **** ADC1 init **** | 14 | // **** ADC1 init **** |
| 15 | let mut adc1 = adc::Adc::new(p.ADC1); | 15 | let mut config = AdcConfig::default(); |
| 16 | config.averaging = Some(adc::Averaging::Samples1024); | ||
| 17 | config.resolution = Some(adc::Resolution::BITS14); | ||
| 18 | let mut adc1 = adc::Adc::new_with_config(p.ADC1, config); | ||
| 16 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 | 19 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 |
| 17 | let mut adc1_pin2 = p.PA2; // A1 | 20 | let mut adc1_pin2 = p.PA2; // A1 |
| 18 | adc1.set_resolution(adc::Resolution::BITS14); | ||
| 19 | adc1.set_averaging(adc::Averaging::Samples1024); | ||
| 20 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 21 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 21 | 22 | ||
| 22 | // **** ADC2 init **** | 23 | // **** ADC2 init **** |
| 23 | let mut adc2 = adc::Adc::new(p.ADC2); | 24 | let mut config = AdcConfig::default(); |
| 25 | config.averaging = Some(adc::Averaging::Samples1024); | ||
| 26 | config.resolution = Some(adc::Resolution::BITS14); | ||
| 27 | let mut adc2 = adc::Adc::new_with_config(p.ADC2, config); | ||
| 24 | let mut adc2_pin1 = p.PC3; // A2 | 28 | let mut adc2_pin1 = p.PC3; // A2 |
| 25 | let mut adc2_pin2 = p.PB0; // A3 | 29 | let mut adc2_pin2 = p.PB0; // A3 |
| 26 | adc2.set_resolution(adc::Resolution::BITS14); | ||
| 27 | adc2.set_averaging(adc::Averaging::Samples1024); | ||
| 28 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 30 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 29 | 31 | ||
| 30 | // **** ADC4 init **** | 32 | // **** ADC4 init **** |
