Skip to content
1 change: 1 addition & 0 deletions embassy-stm32/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- next-header -->
## Unreleased - ReleaseDate

- feat: Added support for injected ADC sampling for g4 ([#4243](https://github.com/embassy-rs/embassy/pull/4243))
- fix: Fixed STM32H5 builds requiring time feature
- feat: Derive Clone, Copy for QSPI Config
- fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received
Expand Down
102 changes: 98 additions & 4 deletions embassy-stm32/src/adc/g4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +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;
Expand All @@ -18,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);
Expand Down Expand Up @@ -354,7 +357,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.
///
Expand Down Expand Up @@ -474,6 +477,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<Item = (&'a mut AnyAdcChannel<T>, 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<T>, sample_time: SampleTime) {
// Configure channel
Self::set_channel_sample_time(channel.channel(), sample_time);
Expand Down
7 changes: 6 additions & 1 deletion embassy-stm32/src/timer/complementary_pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use core::marker::PhantomData;

pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr};
pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr};

use super::low_level::{CountingMode, OutputPolarity, Timer};
use super::simple_pwm::PwmPin;
Expand Down Expand Up @@ -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);
}

/// 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);
Expand Down
4 changes: 4 additions & 0 deletions embassy-stm32/src/timer/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@ 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));
Expand Down