aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-08-25 07:57:22 +0000
committerGitHub <[email protected]>2025-08-25 07:57:22 +0000
commit1975965d7710cd86c7a169a9b5ae6340bddc316c (patch)
tree19fe12b27c1c68103f3f9dd38994e95ad0753dd7
parentef673c6ca310cf0a7e9b1254afb7806bd6879e94 (diff)
parentcfe71df1239a1c50637ca9a9b0412b36f215d7b0 (diff)
Merge pull request #4306 from yodaldevoid/looping-adc-read
stm32/adc/v3: allow DMA reads to loop through enabled channels
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/adc/v3.rs7
-rw-r--r--examples/stm32h5/src/bin/adc_dma.rs94
3 files changed, 99 insertions, 3 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 9cd4d5951..dd82613d9 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13- fix: Fix vrefbuf building with log feature 13- fix: Fix vrefbuf building with log feature
14- fix: Fix performing a hash after performing a hmac 14- fix: Fix performing a hash after performing a hmac
15- chore: Updated stm32-metapac and stm32-data dependencies 15- chore: Updated stm32-metapac and stm32-data dependencies
16- feat: stm32/adc/v3: allow DMA reads to loop through enable channels
16- fix: Fix XSPI not disabling alternate bytes when they were previously enabled 17- fix: Fix XSPI not disabling alternate bytes when they were previously enabled
17 18
18## 0.3.0 - 2025-08-12 19## 0.3.0 - 2025-08-12
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index a2e42fe52..dc1faa4d1 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -296,7 +296,8 @@ impl<'d, T: Instance> Adc<'d, T> {
296 296
297 /// Read one or multiple ADC channels using DMA. 297 /// Read one or multiple ADC channels using DMA.
298 /// 298 ///
299 /// `sequence` iterator and `readings` must have the same length. 299 /// `readings` must have a length that is a multiple of the length of the
300 /// `sequence` iterator.
300 /// 301 ///
301 /// Note: The order of values in `readings` is defined by the pin ADC 302 /// Note: The order of values in `readings` is defined by the pin ADC
302 /// channel number and not the pin order in `sequence`. 303 /// channel number and not the pin order in `sequence`.
@@ -330,8 +331,8 @@ impl<'d, T: Instance> Adc<'d, T> {
330 ) { 331 ) {
331 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); 332 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
332 assert!( 333 assert!(
333 sequence.len() == readings.len(), 334 readings.len() % sequence.len() == 0,
334 "Sequence length must be equal to readings length" 335 "Readings length must be a multiple of sequence length"
335 ); 336 );
336 assert!( 337 assert!(
337 sequence.len() <= 16, 338 sequence.len() <= 16,
diff --git a/examples/stm32h5/src/bin/adc_dma.rs b/examples/stm32h5/src/bin/adc_dma.rs
new file mode 100644
index 000000000..20073e22f
--- /dev/null
+++ b/examples/stm32h5/src/bin/adc_dma.rs
@@ -0,0 +1,94 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime};
7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3};
8use embassy_stm32::{Config, Peri};
9use embassy_time::Instant;
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(spawner: Spawner) {
14 let mut config = Config::default();
15 {
16 use embassy_stm32::rcc::*;
17 config.rcc.hsi = Some(HSIPrescaler::DIV1);
18 config.rcc.csi = true;
19 config.rcc.pll1 = Some(Pll {
20 source: PllSource::HSI,
21 prediv: PllPreDiv::DIV4,
22 mul: PllMul::MUL25,
23 divp: Some(PllDiv::DIV2),
24 divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q
25 divr: None,
26 });
27 config.rcc.pll2 = Some(Pll {
28 source: PllSource::HSI,
29 prediv: PllPreDiv::DIV4,
30 mul: PllMul::MUL25,
31 divp: None,
32 divq: None,
33 divr: Some(PllDiv::DIV4), // 100mhz
34 });
35 config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz
36 config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz
37 config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
38 config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
39 config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
40 config.rcc.voltage_scale = VoltageScale::Scale1;
41 config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;
42 }
43 let p = embassy_stm32::init(config);
44
45 spawner.must_spawn(adc1_task(p.ADC1, p.GPDMA1_CH0, p.PA0, p.PA2));
46 spawner.must_spawn(adc2_task(p.ADC2, p.GPDMA1_CH1, p.PA1, p.PA3));
47}
48
49#[embassy_executor::task]
50async fn adc1_task(
51 adc: Peri<'static, ADC1>,
52 dma: Peri<'static, GPDMA1_CH0>,
53 pin1: Peri<'static, PA0>,
54 pin2: Peri<'static, PA2>,
55) {
56 adc_task(adc, dma, pin1, pin2).await;
57}
58
59#[embassy_executor::task]
60async fn adc2_task(
61 adc: Peri<'static, ADC2>,
62 dma: Peri<'static, GPDMA1_CH1>,
63 pin1: Peri<'static, PA1>,
64 pin2: Peri<'static, PA3>,
65) {
66 adc_task(adc, dma, pin1, pin2).await;
67}
68
69async fn adc_task<'a, T: adc::Instance>(
70 adc: Peri<'a, T>,
71 mut dma: Peri<'a, impl RxDma<T>>,
72 pin1: impl AdcChannel<T>,
73 pin2: impl AdcChannel<T>,
74) {
75 let mut adc = Adc::new(adc);
76 let mut pin1 = pin1.degrade_adc();
77 let mut pin2 = pin2.degrade_adc();
78
79 let mut tic = Instant::now();
80 let mut buffer = [0u16; 512];
81 loop {
82 // This is not a true continuous read as there is downtime between each
83 // call to `Adc::read` where the ADC is sitting idle.
84 adc.read(
85 dma.reborrow(),
86 [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(),
87 &mut buffer,
88 )
89 .await;
90 let toc = Instant::now();
91 info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros());
92 tic = toc;
93 }
94}