From 1bff85649ecff00ccdda328af1c30bf78264434e Mon Sep 17 00:00:00 2001 From: Jakob Date: Fri, 23 May 2025 17:09:34 +0200 Subject: [PATCH 1/9] Add support for dual adc and injected sampling based on interrupts --- embassy-stm32/src/adc/g4.rs | 103 ++++++++++++++++++- embassy-stm32/src/timer/complementary_pwm.rs | 7 +- embassy-stm32/src/timer/low_level.rs | 5 + 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 8ed102c1bf..d1ad464b4c 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -3,10 +3,10 @@ use pac::adc::vals::{Adcaldif, Difsel, Exten}; #[allow(unused)] #[cfg(stm32g4)] -use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; -use pac::adccommon::vals::Presc; -use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; - +pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; +pub use pac::adccommon::vals::Presc; +pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; +pub use stm32_metapac::adccommon::vals::Dual; use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; use crate::adc::SealedAdcChannel; use crate::dma::Transfer; @@ -34,6 +34,8 @@ const VREF_CHANNEL: u8 = 19; #[cfg(stm32h7)] const TEMP_CHANNEL: u8 = 18; +const NR_INJECTED_RANKS: u8 = 4; + // TODO this should be 14 for H7a/b/35 const VBAT_CHANNEL: u8 = 17; @@ -358,7 +360,7 @@ impl<'d, T: Instance> Adc<'d, T> { self.read_channel(channel) } - /// Read one or multiple ADC channels using DMA. + /// Read one or multiple ADC regular channels using DMA. /// /// `sequence` iterator and `readings` must have the same length. /// @@ -478,6 +480,97 @@ impl<'d, T: Instance> Adc<'d, T> { }); } + // Dual ADC mode selection + pub fn configure_dual_mode(&mut self, val: Dual) { + T::common_regs().ccr().modify(|reg| { + reg.set_dual(val); + }) + } + + /// Configure a sequence of injected channels + pub fn configure_injected_sequence<'a>( + &mut self, + sequence: impl ExactSizeIterator, SampleTime)>, + ) { + assert!(sequence.len() != 0, "Read sequence cannot be empty"); + assert!( + sequence.len() <= NR_INJECTED_RANKS, + "Read sequence cannot be more than 4 in length" + ); + + // Ensure no conversions are ongoing and ADC is enabled. + Self::cancel_conversions(); + self.enable(); + + // Set sequence length + T::regs().jsqr().modify(|w| { + w.set_jl(sequence.len() as u8 - 1); + }); + + // Configure channels and ranks + for (n, (channel, sample_time)) in sequence.enumerate() { + Self::configure_channel(channel, sample_time); + + match n { + 0..=3 => { + T::regs().jsqr().modify(|w| { + w.set_jsq(n, channel.channel()); + }); + } + 4..=8 => { + T::regs().jsqr().modify(|w| { + w.set_jsq(n - 4, channel.channel()); + }); + } + 9..=13 => { + T::regs().jsqr().modify(|w| { + w.set_jsq(n - 9, channel.channel()); + }); + } + 14..=15 => { + T::regs().jsqr().modify(|w| { + w.set_jsq(n - 14, channel.channel()); + }); + } + _ => unreachable!(), + } + } + + T::regs().cfgr().modify(|reg| { + reg.set_discen(false); + reg.set_cont(false); // False for interrupt triggered measurements + reg.set_dmacfg(Dmacfg::ONE_SHOT); + reg.set_dmaen(Dmaen::DISABLE); + }); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_jadstart(true); + }); + } + + /// Set external trigger for injected conversion sequence + pub fn set_injected_conversion_trigger(&mut self, trigger: u8, edge: Exten) { + T::regs().jsqr().modify(|r| { + r.set_jextsel(trigger); // ADC group injected external trigger source + r.set_jexten(edge); // ADC group injected external trigger polarity + }); + T::regs().ier().modify(|r| r.set_jeosie(true)); // group injected end of sequence conversions interrupt + } + + /// Read sampled data from all injected ADC injected ranks + /// Clear the JEOS flag to allow a new injected sequence + pub fn clear_injected_eos(&mut self) -> [u16; NR_INJECTED_RANKS] { + let mut data = [0u16; NR_INJECTED_RANKS]; + for i in 0..NR_INJECTED_RANKS { + data[i] = T::regs().jdr(i).read().jdata(); + } + + // Clear JEOS by writing 1 + T::regs().isr().modify(|r| r.set_jeos(true)); + data + } + fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { // Configure channel Self::set_channel_sample_time(channel.channel(), sample_time); diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 8eec6c0c71..157f12c492 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; -use stm32_metapac::timer::vals::Ckd; +pub use stm32_metapac::timer::vals::{Ckd, Mms2}; use super::low_level::{CountingMode, OutputPolarity, Timer}; use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; @@ -92,6 +92,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { this } + /// Set Master Slave Mode 2 + pub fn set_mms2(&mut self, mms2: Mms2) { + self.inner.set_mms2_selection(mms2); + } + /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index dc8ceb7254..dbdecac5e5 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -725,4 +725,9 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { .ccer() .modify(|w| w.set_ccne(channel.index(), enable)); } + + /// Set master mode selection 2 + pub fn set_mms2_selection(&self, mms2: vals::Mms2) { + self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); + } } From 176fa86dc55e67da3637d3afbbed838e3c57459b Mon Sep 17 00:00:00 2001 From: Jakob Date: Fri, 23 May 2025 17:15:04 +0200 Subject: [PATCH 2/9] Fix datatype u8 to usize --- embassy-stm32/src/adc/g4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index d1ad464b4c..19ed2ccbf8 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -34,7 +34,7 @@ const VREF_CHANNEL: u8 = 19; #[cfg(stm32h7)] const TEMP_CHANNEL: u8 = 18; -const NR_INJECTED_RANKS: u8 = 4; +const NR_INJECTED_RANKS: usize = 4; // TODO this should be 14 for H7a/b/35 const VBAT_CHANNEL: u8 = 17; From edbe1d2700c959f46fda2a7735a664e77c1d97bc Mon Sep 17 00:00:00 2001 From: Jakob Date: Fri, 23 May 2025 17:16:30 +0200 Subject: [PATCH 3/9] Fix formatting --- embassy-stm32/src/adc/g4.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 19ed2ccbf8..0a51aacb5e 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -7,6 +7,7 @@ pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; pub use pac::adccommon::vals::Presc; pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; pub use stm32_metapac::adccommon::vals::Dual; + use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; use crate::adc::SealedAdcChannel; use crate::dma::Transfer; @@ -542,7 +543,7 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_dmacfg(Dmacfg::ONE_SHOT); reg.set_dmaen(Dmaen::DISABLE); }); - + // Start conversion T::regs().cr().modify(|reg| { reg.set_jadstart(true); From a8098f161fc900b1ac54c0feda74d2aedd8f912e Mon Sep 17 00:00:00 2001 From: Jakob <62303446+Jacke-debug@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:33:20 +0200 Subject: [PATCH 4/9] Re-add accidentally removed constant --- embassy-stm32/src/adc/g4.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 32b8ff4dbd..e2f938e480 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -19,6 +19,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; +const NR_INJECTED_RANKS: usize = 4; + /// Max single ADC operation clock frequency #[cfg(stm32g4)] const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); From 7b2ec3c3324e3827fc1225eeda922b167958f934 Mon Sep 17 00:00:00 2001 From: Jakob Date: Fri, 15 Aug 2025 16:48:15 +0200 Subject: [PATCH 5/9] Add missing { --- embassy-stm32/src/timer/complementary_pwm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 93b166f18e..a31d64ba0a 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -94,6 +94,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Set Master Slave Mode 2 pub fn set_mms2(&mut self, mms2: Mms2) { self.inner.set_mms2_selection(mms2); + } /// Sets the idle output state for the given channels. pub fn set_output_idle_state(&mut self, channels: &[Channel], polarity: IdlePolarity) { From ea004e598cc5b3767782ab455196d774b3624553 Mon Sep 17 00:00:00 2001 From: Jakob Date: Fri, 15 Aug 2025 16:52:33 +0200 Subject: [PATCH 6/9] Add changelog entry --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 0c2d232466..9bca66df7d 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: Added support for injected ADC sampling for g4 ([#4243](https://github.com/embassy-rs/embassy/pull/4243)) - fix: Fix vrefbuf building with log feature ## 0.3.0 - 2025-08-12 From 0df63541696c4fc1d4c2ceffe56bc748456d465e Mon Sep 17 00:00:00 2001 From: Jakob Date: Mon, 6 Oct 2025 16:57:06 +0200 Subject: [PATCH 7/9] Remove incorrect config of registers connected to regular conversions --- embassy-stm32/src/adc/g4.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index e2f938e480..830f898f93 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -534,10 +534,7 @@ impl<'d, T: Instance> Adc<'d, T> { } T::regs().cfgr().modify(|reg| { - reg.set_discen(false); - reg.set_cont(false); // False for interrupt triggered measurements - reg.set_dmacfg(Dmacfg::ONE_SHOT); - reg.set_dmaen(Dmaen::DISABLE); + reg.set_jdiscen(false); // Will convert all channels for each trigger }); // Start conversion From 275dd3305a71628e289d519453d30b4f52ef7676 Mon Sep 17 00:00:00 2001 From: Jakob Date: Tue, 28 Oct 2025 20:59:54 +0100 Subject: [PATCH 8/9] Move enabling of end of conversion sequence interrupt to a separate method for more flexibility. --- embassy-stm32/src/adc/g4.rs | 6 +++++- embassy-stm32/src/timer/complementary_pwm.rs | 15 +++++++++----- embassy-stm32/src/timer/low_level.rs | 21 +++++++++++++++----- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 6488f65875..8bc49b7f22 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -552,7 +552,11 @@ impl<'d, T: Instance> Adc<'d, T> { r.set_jextsel(trigger); // ADC group injected external trigger source r.set_jexten(edge); // ADC group injected external trigger polarity }); - T::regs().ier().modify(|r| r.set_jeosie(true)); // group injected end of sequence conversions interrupt + } + + /// Enable end of injected sequence interrupt + pub fn enable_injected_eos_interrupt(&mut self, enable: bool) { + T::regs().ier().modify(|r| r.set_jeosie(enable)); } /// Read sampled data from all injected ADC injected ranks diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 725b7d4a06..9a56a41fb9 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -92,11 +92,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { this } - /// Set Master Slave Mode 2 - pub fn set_mms2(&mut self, mms2: Mms2) { - self.inner.set_mms2_selection(mms2); - } - /// Sets the idle output state for the given channels. pub fn set_output_idle_state(&mut self, channels: &[Channel], polarity: IdlePolarity) { let ois_active = matches!(polarity, IdlePolarity::OisActive); @@ -141,6 +136,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { self.inner.get_moe() } + /// Set Master Slave Mode 2 + pub fn set_mms2(&mut self, mms2: Mms2) { + self.inner.set_mms2_selection(mms2); + } + + /// Set Repetition Counter + pub fn set_repetition_counter(&mut self, val: u16) { + self.inner.set_repetition_counter(val); + } + /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index e9d7cc662d..de23d89143 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -10,7 +10,7 @@ use core::mem::ManuallyDrop; use embassy_hal_internal::Peri; // Re-export useful enums -pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; +pub use stm32_metapac::timer::vals::{FilterValue, Mms, Mms2, Sms as SlaveMode, Ts as TriggerSource}; use super::*; use crate::pac::timer::vals; @@ -574,6 +574,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { .modify(|w| w.set_ccp(channel.index(), polarity.into())); } + /// Set master mode selection + pub fn set_mms_selection(&self, mms: vals::Mms) { + self.regs_gp16().cr2().modify(|w| w.set_mms(mms)); + } + /// Enable/disable a channel. pub fn enable_channel(&self, channel: Channel, enable: bool) { self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); @@ -751,10 +756,6 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { .modify(|w| w.set_ccne(channel.index(), enable)); } - /// Set master mode selection 2 - pub fn set_mms2_selection(&self, mms2: vals::Mms2) { - self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); - } /// Set Output Idle State pub fn set_ois(&self, channel: Channel, val: bool) { self.regs_advanced().cr2().modify(|w| w.set_ois(channel.index(), val)); @@ -764,6 +765,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); } + /// Set master mode selection 2 + pub fn set_mms2_selection(&self, mms2: vals::Mms2) { + self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); + } + + /// Set repetition counter + pub fn set_repetition_counter(&self, val: u16) { + self.regs_advanced().rcr().modify(|w| w.set_rep(val)); + } + /// Trigger software break 1 or 2 /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. pub fn trigger_software_break(&self, n: usize) { From f35cf3a63817ee5b27f8554b8b407bfc9a8f915f Mon Sep 17 00:00:00 2001 From: Jakob Date: Tue, 28 Oct 2025 21:07:13 +0100 Subject: [PATCH 9/9] Fix import error --- embassy-stm32/src/timer/low_level.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index de23d89143..dbf571732e 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -10,7 +10,7 @@ use core::mem::ManuallyDrop; use embassy_hal_internal::Peri; // Re-export useful enums -pub use stm32_metapac::timer::vals::{FilterValue, Mms, Mms2, Sms as SlaveMode, Ts as TriggerSource}; +pub use stm32_metapac::timer::vals::{FilterValue, Mms, Sms as SlaveMode, Ts as TriggerSource}; use super::*; use crate::pac::timer::vals;