diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9c2ba1f53..6cd8ed262 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -174,7 +174,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "18" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d8432edd0406495adec19d31923584e80b8e03cb" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7" } vcell = "0.1.3" nb = "1.0.0" @@ -192,6 +192,7 @@ bitflags = "2.4.2" block-device-driver = { version = "0.2" } aligned = "0.4.1" +heapless = "0.9.1" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -203,7 +204,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d8432edd0406495adec19d31923584e80b8e03cb", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 77f24c87f..eb8b53495 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,8 +1,13 @@ use cfg_if::cfg_if; use pac::adc::vals::Dmacfg; +#[cfg(adc_g0)] +use pac::adc::vals::{Ckmode, Ovsr, Ovss, Presc, Smpsel}; #[cfg(adc_v3)] use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; +#[cfg(adc_g0)] +use heapless::Vec; + use super::{ blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, }; @@ -14,6 +19,11 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3000; +#[cfg(adc_g0)] +/// The number of variants in Smpsel +// TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable +const SAMPLE_TIMES_CAPACITY: usize = 2; + pub struct VrefInt; impl AdcChannel for VrefInt {} impl SealedAdcChannel for VrefInt { @@ -111,30 +121,10 @@ pub enum Averaging { cfg_if! { if #[cfg(adc_g0)] { /// Synchronous PCLK prescaler -/// * ADC_CFGR2:CKMODE in STM32WL5x -#[repr(u8)] pub enum CkModePclk { - DIV1 = 3, - DIV2 = 1, - DIV4 = 2, -} - -/// Asynchronous ADCCLK prescaler -/// * ADC_CCR:PRESC in STM32WL5x -#[repr(u8)] -pub enum Presc { DIV1, DIV2, DIV4, - DIV6, - DIV8, - DIV10, - DIV12, - DIV16, - DIV32, - DIV64, - DIV128, - DIV256, } /// The analog clock is either the synchronous prescaled PCLK or @@ -213,8 +203,14 @@ impl<'d, T: Instance> Adc<'d, T> { } } match clock { - Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div as u8)), - Clock::Sync { div } => T::regs().cfgr2().modify(|reg| reg.set_ckmode(div as u8)), + Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), + Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { + reg.set_ckmode(match div { + CkModePclk::DIV1 => Ckmode::PCLK, + CkModePclk::DIV2 => Ckmode::PCLK_DIV2, + CkModePclk::DIV4 => Ckmode::PCLK_DIV4, + }) + }), } Self::init_calibrate(); @@ -434,53 +430,78 @@ impl<'d, T: Instance> Adc<'d, T> { w.set_l(sequence.len() as u8 - 1); }); - #[cfg(any(adc_g0, adc_u0))] - let mut channel_mask = 0; + #[cfg(adc_g0)] + { + let mut sample_times = Vec::::new(); - // Configure channels and ranks - for (_i, (channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(channel, sample_time); - - // Each channel is sampled according to sequence - #[cfg(not(any(adc_g0, adc_u0)))] - match _i { - 0..=3 => { - T::regs().sqr1().modify(|w| { - w.set_sq(_i, channel.channel()); - }); - } - 4..=8 => { - T::regs().sqr2().modify(|w| { - w.set_sq(_i - 4, channel.channel()); - }); - } - 9..=13 => { - T::regs().sqr3().modify(|w| { - w.set_sq(_i - 9, channel.channel()); - }); - } - 14..=15 => { - T::regs().sqr4().modify(|w| { - w.set_sq(_i - 14, channel.channel()); - }); - } - _ => unreachable!(), - } - - #[cfg(any(adc_g0, adc_u0))] - { - channel_mask |= 1 << channel.channel(); - } + T::regs().chselr().write(|chselr| { + T::regs().smpr().write(|smpr| { + for (channel, sample_time) in sequence { + chselr.set_chsel(channel.channel.into(), true); + if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { + smpr.set_smpsel(channel.channel.into(), (i as u8).into()); + } else { + smpr.set_sample_time(sample_times.len(), sample_time); + if let Err(_) = sample_times.push(sample_time) { + panic!( + "Implementation is limited to {} unique sample times among all channels.", + SAMPLE_TIMES_CAPACITY + ); + } + } + } + }) + }); } + #[cfg(not(adc_g0))] + { + #[cfg(adc_u0)] + let mut channel_mask = 0; - // On G0 and U0 enabled channels are sampled from 0 to last channel. - // It is possible to add up to 8 sequences if CHSELRMOD = 1. - // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. - #[cfg(any(adc_g0, adc_u0))] - T::regs().chselr().modify(|reg| { - reg.set_chsel(channel_mask); - }); + // Configure channels and ranks + for (_i, (channel, sample_time)) in sequence.enumerate() { + Self::configure_channel(channel, sample_time); + // Each channel is sampled according to sequence + #[cfg(not(any(adc_g0, adc_u0)))] + match _i { + 0..=3 => { + T::regs().sqr1().modify(|w| { + w.set_sq(_i, channel.channel()); + }); + } + 4..=8 => { + T::regs().sqr2().modify(|w| { + w.set_sq(_i - 4, channel.channel()); + }); + } + 9..=13 => { + T::regs().sqr3().modify(|w| { + w.set_sq(_i - 9, channel.channel()); + }); + } + 14..=15 => { + T::regs().sqr4().modify(|w| { + w.set_sq(_i - 14, channel.channel()); + }); + } + _ => unreachable!(), + } + + #[cfg(adc_u0)] + { + channel_mask |= 1 << channel.channel(); + } + } + + // On G0 and U0 enabled channels are sampled from 0 to last channel. + // It is possible to add up to 8 sequences if CHSELRMOD = 1. + // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. + #[cfg(adc_u0)] + T::regs().chselr().modify(|reg| { + reg.set_chsel(channel_mask); + }); + } // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { @@ -535,6 +556,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } + #[cfg(not(adc_g0))] fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." @@ -549,13 +571,23 @@ impl<'d, T: Instance> Adc<'d, T> { fn read_channel(&mut self, channel: &mut impl AdcChannel) -> u16 { self.enable(); + #[cfg(not(adc_g0))] Self::configure_channel(channel, self.sample_time); - + #[cfg(adc_g0)] + T::regs().smpr().write(|reg| { + reg.set_sample_time(0, self.sample_time); + reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); + }); // Select channel #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); #[cfg(any(adc_g0, adc_u0))] - T::regs().chselr().write(|reg| reg.set_chsel(1 << channel.channel())); + T::regs().chselr().write(|reg| { + #[cfg(adc_g0)] + reg.set_chsel(channel.channel().into(), true); + #[cfg(adc_u0)] + reg.set_chsel(1 << channel.channel()); + }); // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be @@ -579,12 +611,20 @@ impl<'d, T: Instance> Adc<'d, T> { val } - #[cfg(any(adc_g0, adc_u0))] + #[cfg(adc_g0)] + pub fn set_oversampling_shift(&mut self, shift: Ovss) { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + #[cfg(adc_u0)] pub fn set_oversampling_shift(&mut self, shift: u8) { T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); } - #[cfg(any(adc_g0, adc_u0))] + #[cfg(adc_g0)] + pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + #[cfg(adc_u0)] pub fn set_oversampling_ratio(&mut self, ratio: u8) { T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); } @@ -611,9 +651,10 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); } + #[cfg(not(adc_g0))] fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { + if #[cfg(adc_u0)] { // On G0 and U6 all channels use the same sampling time. T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } else if #[cfg(any(adc_h5, adc_h7rs))] {