Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
484410c
Add method to enable interrupts in complementary pwm
Jacke-debug Apr 28, 2025
787fed4
Add complementary pwm method to clear interrupt flag
Jacke-debug May 5, 2025
8b5ae35
Add complementary pwm method to clear interrupt flag
Jacke-debug May 5, 2025
63ae8b9
Merge branch 'main' of https://github.com/Jacke-debug/embassy
Jacke-debug May 5, 2025
d1ac54a
Make rx_dma mutable in g4s adc read method
Jacke-debug May 5, 2025
4d67f38
Revert "Make rx_dma mutable in g4s adc read method"
Jacke-debug May 5, 2025
1bff856
Add support for dual adc and injected sampling based on interrupts
Jacke-debug May 23, 2025
176fa86
Fix datatype u8 to usize
Jacke-debug May 23, 2025
edbe1d2
Fix formatting
Jacke-debug May 23, 2025
415b058
Add function to enable atuoreload_preload
Jacke-debug Jun 9, 2025
084a839
Add method for setting preload enable for the compare channels
Jacke-debug Jun 9, 2025
f68ec82
Add code so setting autoreload also triggers update event
Jacke-debug Jun 9, 2025
0c8934d
Fix index in preload_enable
Jacke-debug Jun 9, 2025
18ae044
Add method for setting repetition counter
Jacke-debug Jun 9, 2025
757d783
Remove duplicated code
Jacke-debug Jun 11, 2025
1088056
Merge branch 'main' of https://github.com/Jacke-debug/embassy
Jacke-debug Jun 11, 2025
e30e344
Update get_max_duty to handle center aligned PWM mode correctly
Jacke-debug Jun 11, 2025
2388860
Merge branch 'timer_auto_reload'
Jacke-debug Jun 29, 2025
13dc86e
Add complementary pwm method for setting moe bit
Jacke-debug Jun 29, 2025
66ea955
stm32/adc/v3: Add method to sample ADC without reconfiguring every time
clubby789 Jul 23, 2025
1e4ba80
Merge branch 'main' into add_support_for_dual_mode_injected_sampling
Jacke-debug Jul 30, 2025
a8098f1
Re-add accidentally removed constant
Jacke-debug Jul 30, 2025
5a0a6f5
Merge branch 'main' into add_support_for_dual_mode_injected_sampling
Jacke-debug Aug 3, 2025
109cc01
Add methods for setting ossi, ossr, osi and oisn along with software …
Jacke-debug Aug 6, 2025
18d6071
Add get methods for meo, ossi and ossr
Jacke-debug Aug 6, 2025
26822b1
Change method arugments to be non-mutable
Jacke-debug Aug 6, 2025
b627ab6
Add enum for IdlePolarity to make interface clearer for set_output_id…
Jacke-debug Aug 8, 2025
6b05e6c
Adjust which methods should have mut reference to self. Change set_ou…
Jacke-debug Aug 9, 2025
4cef0e4
Merge remote-tracking branch 'origin/main' into add_support_for_dual_…
Jacke-debug Aug 10, 2025
2d51551
Merge branch 'main' into add_support_for_dual_mode_injected_sampling
Jacke-debug Aug 15, 2025
7b2ec3c
Add missing {
Jacke-debug Aug 15, 2025
ea004e5
Add changelog entry
Jacke-debug Aug 15, 2025
67cf703
Merge branch 'main' into add_support_for_dual_mode_injected_sampling
Jacke-debug Aug 25, 2025
0947061
Merge branch 'main' into add_support_for_dual_mode_injected_sampling
lulf Sep 17, 2025
387ea98
Remove incorrect config for regular channels in injected channel setup
Jacke-debug Oct 6, 2025
060bb35
Merge branch 'add_support_for_dual_mode_injected_sampling' of https:/…
Jacke-debug Oct 6, 2025
0df6354
Remove incorrect config of registers connected to regular conversions
Jacke-debug Oct 6, 2025
024b922
Merge branch 'main' into add_support_for_dual_mode_injected_sampling
Jacke-debug Oct 19, 2025
88f1002
Merge branch 'main' into regular_and_injected_adc
Jacke-debug Oct 19, 2025
dce2dd5
Merge branch 'add_support_for_dual_mode_injected_sampling' into regul…
Jacke-debug Oct 19, 2025
de442a1
Break up regular ADC reading method for g4 in two parts to avoid reco…
Jacke-debug Oct 19, 2025
9ebd5cb
break apart methods
Jacke-debug Oct 19, 2025
0327708
Add missing import
Jacke-debug Oct 19, 2025
4eb4560
Jadstp uses adstp enum
Jacke-debug Oct 19, 2025
e9009aa
Check length condition outside of setup
Jacke-debug Oct 19, 2025
b9571bf
Regular and inejcted conversions now work based on timer trigger usin…
Jacke-debug Oct 24, 2025
05ed966
Cleanup code and add doc comments.
Jacke-debug Oct 25, 2025
935bfdf
Restore cargo config settings in example code.
Jacke-debug Oct 25, 2025
e32508c
Restore adc v3
Jacke-debug Oct 25, 2025
7a7fe46
cargo fmt
Jacke-debug Oct 25, 2025
3838109
Merge remote-tracking branch 'origin/main' into complementary_pwm_asc
Jacke-debug Oct 25, 2025
00e997c
Fix missed merge conflict
Jacke-debug Oct 25, 2025
ac1b2da
Merge branch 'complementary_pwm_asc' into regular_and_injected_adc
Jacke-debug Oct 25, 2025
f46805b
Add method for setting Mms for GeneralInstance4Channel timer to be ab…
Jacke-debug Oct 25, 2025
1b4e4c0
Use read_exact instead in example to code to simplify loop timing
Jacke-debug Oct 26, 2025
275dd33
Move enabling of end of conversion sequence interrupt to a separate m…
Jacke-debug Oct 28, 2025
909c80f
Merge commit '275dd33' into add_support_for_dual_mode_injected_sampling
Jacke-debug Oct 28, 2025
329ac91
Fix import error
Jacke-debug Oct 28, 2025
e561fe8
Fix duplicate method
Jacke-debug Oct 28, 2025
819a52b
Merge branch 'add_support_for_dual_mode_injected_sampling' into regul…
Jacke-debug Oct 28, 2025
2363284
Add enabling of adc interrupt to example
Jacke-debug Oct 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions embassy-stm32/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* **Fix(stm32h5):** Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)
## Unreleased - ReleaseDate

- feat: Rework regular ADC conversions for g4 to allow external trigger and avoid ADC reconfiguration for each conversion ([#4786](https://github.com/embassy-rs/embassy/pull/4786))
- feat: Added support for injected ADC sampling for g4 ([#4243](https://github.com/embassy-rs/embassy/pull/4243))
- fix flash erase on L4 & L5
- fix: Fixed STM32H5 builds requiring time feature
- feat: Derive Clone, Copy for QSPI Config
Expand Down
206 changes: 174 additions & 32 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::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us};
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 @@ -357,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.
///
Expand All @@ -382,24 +385,63 @@ impl<'d, T: Instance> Adc<'d, T> {
/// .await;
/// defmt::info!("measurements: {}", measurements);
/// ```
///
/// Note: This is not very efficient as the ADC needs to be reconfigured for each read.
pub async fn read(
&mut self,
rx_dma: Peri<'_, impl RxDma<T>>,
sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
readings: &mut [u16],
) {
assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
assert!(
sequence.len() == readings.len(),
"Sequence length must be equal to readings length"
);
self.configure_regular_sequence(sequence);

// Use ONE_SHOT DMA in order to make a single measuremnt.
T::regs().cfgr().modify(|reg| {
reg.set_dmacfg(Dmacfg::ONE_SHOT);
});

let request = rx_dma.request();
let transfer = unsafe {
Transfer::new_read(
rx_dma,
request,
T::regs().dr().as_ptr() as *mut u16,
readings,
Default::default(),
)
};

// Start conversion
T::regs().cr().modify(|reg| {
reg.set_adstart(true);
});

// Wait for conversion sequence to finish.
transfer.await;

// Ensure conversions are finished.
Self::cancel_regular_conversions();

// Reset configuration.
T::regs().cfgr().modify(|reg| {
reg.set_cont(false);
});
}

pub fn configure_regular_sequence<'a>(
&mut self,
sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
) {
assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
assert!(
sequence.len() <= 16,
"Asynchronous read sequence cannot be more than 16 in length"
);

// Ensure no conversions are ongoing and ADC is enabled.
Self::cancel_conversions();
Self::cancel_regular_conversions();
Self::cancel_injected_conversions();
self.enable();

// Set sequence length
Expand Down Expand Up @@ -436,47 +478,138 @@ impl<'d, T: Instance> Adc<'d, T> {
}
}

// Set continuous mode with oneshot dma.
// Clear overrun flag before starting transfer.
T::regs().isr().modify(|reg| {
reg.set_ovr(true);
});

T::regs().cfgr().modify(|reg| {
reg.set_discen(false);
reg.set_cont(true);
reg.set_dmacfg(Dmacfg::ONE_SHOT);
reg.set_discen(false); // Convert all channels for each trigger
reg.set_cont(false); // New trigger is neede for each sample to be read
reg.set_dmacfg(Dmacfg::CIRCULAR);
reg.set_dmaen(Dmaen::ENABLE);
});
}

let request = rx_dma.request();
let transfer = unsafe {
Transfer::new_read(
rx_dma,
request,
T::regs().dr().as_ptr() as *mut u16,
readings,
Default::default(),
)
};

// Start conversion
// Start regular ADC conversion
pub fn start_regular_conversion(&mut self) {
T::regs().cr().modify(|reg| {
reg.set_adstart(true);
});
}

// Wait for conversion sequence to finish.
transfer.await;
/// Set external trigger for regular conversion sequence
/// Possible trigger values are seen in Table 166 in RM0440 Rev 9
pub fn set_regular_conversion_trigger(&mut self, trigger: u8, edge: Exten) {
T::regs().cfgr().modify(|r| {
r.set_extsel(trigger);
r.set_exten(edge);
});
// Regular conversions uses DMA so no need to generate interrupt
T::regs().ier().modify(|r| r.set_eosie(false));
}

// Ensure conversions are finished.
Self::cancel_conversions();
/// Get Data register address for use with DMA
pub fn get_data_register_address(&mut self) -> *mut u16 {
T::regs().dr().as_ptr() as *mut u16
}

// 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_regular_conversions();
Self::cancel_injected_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!(),
}
}

// Reset configuration.
T::regs().cfgr().modify(|reg| {
reg.set_cont(false);
reg.set_jdiscen(false); // Will convert all channels for each trigger
});
}

/// Start injected ADC conversion
pub fn start_injected_conversion(&mut self) {
T::regs().cr().modify(|reg| {
reg.set_jadstart(true);
});
}

/// Set external trigger for injected conversion sequence
/// Possible trigger values are seen in Table 167 in RM0440 Rev 9
pub fn set_injected_conversion_trigger(&mut self, trigger: u8, edge: Exten) {
T::regs().jsqr().modify(|r| {
r.set_jextsel(trigger);
r.set_jexten(edge);
});
}

/// 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
/// 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 Expand Up @@ -509,14 +642,23 @@ impl<'d, T: Instance> Adc<'d, T> {
}
}

fn cancel_conversions() {
fn cancel_regular_conversions() {
if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
T::regs().cr().modify(|reg| {
reg.set_adstp(Adstp::STOP);
});
while T::regs().cr().read().adstart() {}
}
}

fn cancel_injected_conversions() {
if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
T::regs().cr().modify(|reg| {
reg.set_jadstp(Adstp::STOP);
});
while T::regs().cr().read().jadstart() {}
}
}
}

/// Implemented for ADCs that have a Temperature channel
Expand Down
12 changes: 11 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 @@ -136,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);
Expand Down
17 changes: 16 additions & 1 deletion embassy-stm32/src/timer/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, Sms as SlaveMode, Ts as TriggerSource};

use super::*;
use crate::pac::timer::vals;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -760,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) {
Expand Down
13 changes: 7 additions & 6 deletions examples/stm32g4/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ publish = false

[dependencies]
# Change stm32g491re to your chip name, if necessary.
embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] }
embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
embassy-stm32 = { path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
embassy-sync = { path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { path = "../../embassy-usb", features = ["defmt"] }
embassy-futures = { path = "../../embassy-futures" }
usbd-hid = "0.8.1"

defmt = "1.0.1"
Expand All @@ -25,6 +25,7 @@ embedded-can = { version = "0.4" }
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
heapless = { version = "0.8", default-features = false }
static_cell = "2.0.0"
critical-section = "1.1"

[profile.release]
debug = 2
Expand Down
Loading