aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-04 01:52:42 +0000
committerGitHub <[email protected]>2025-11-04 01:52:42 +0000
commita60768c63826e1ce266891e06a02aba5993a040e (patch)
tree5bc95a644960e63fd0af78165e4b204e5b6f6662
parent345efc383fa9afabaf23b629f0937855ea4c754f (diff)
parent06f6555324e36ae49b3832d7a3f97de5693483fa (diff)
Merge pull request #4832 from xoviat/adc
stm32/adc: merge v2 and v3 ringbuffers
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs (renamed from embassy-stm32/src/adc/ringbuffered_v3.rs)93
-rw-r--r--embassy-stm32/src/adc/ringbuffered_v2.rs432
-rw-r--r--embassy-stm32/src/adc/v2.rs134
-rw-r--r--embassy-stm32/src/adc/v3.rs36
-rw-r--r--examples/stm32f4/src/bin/adc_dma.rs29
6 files changed, 232 insertions, 493 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 71989eb3d..c717235e2 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38- fix: usart: fix race condition in ringbuffered usart 38- fix: usart: fix race condition in ringbuffered usart
39- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) 39- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821))
40- low-power: update rtc api to allow reconfig 40- low-power: update rtc api to allow reconfig
41- adc: consolidate ringbuffer
41- feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716)) 42- feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716))
42 43
43## 0.4.0 - 2025-08-26 44## 0.4.0 - 2025-08-26
diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered.rs
index 0aee309e3..1f384efb5 100644
--- a/embassy-stm32/src/adc/ringbuffered_v3.rs
+++ b/embassy-stm32/src/adc/ringbuffered.rs
@@ -1,9 +1,13 @@
1use core::marker::PhantomData; 1use core::marker::PhantomData;
2use core::sync::atomic::{Ordering, compiler_fence}; 2use core::sync::atomic::{Ordering, compiler_fence};
3 3
4#[allow(unused_imports)]
4use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
5 6
7use crate::adc::Adc;
8#[allow(unused_imports)]
6use crate::adc::{Instance, RxDma}; 9use crate::adc::{Instance, RxDma};
10#[allow(unused_imports)]
7use crate::dma::{ReadableRingBuffer, TransferOptions}; 11use crate::dma::{ReadableRingBuffer, TransferOptions};
8use crate::rcc; 12use crate::rcc;
9 13
@@ -11,8 +15,8 @@ use crate::rcc;
11pub struct OverrunError; 15pub struct OverrunError;
12 16
13pub struct RingBufferedAdc<'d, T: Instance> { 17pub struct RingBufferedAdc<'d, T: Instance> {
14 pub _phantom: PhantomData<T>, 18 _phantom: PhantomData<T>,
15 pub ring_buf: ReadableRingBuffer<'d, u16>, 19 ring_buf: ReadableRingBuffer<'d, u16>,
16} 20}
17 21
18impl<'d, T: Instance> RingBufferedAdc<'d, T> { 22impl<'d, T: Instance> RingBufferedAdc<'d, T> {
@@ -36,49 +40,26 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
36 } 40 }
37 } 41 }
38 42
39 #[inline] 43 /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.
40 fn start_continuous_sampling(&mut self) { 44 pub fn start(&mut self) {
41 // Start adc conversion 45 compiler_fence(Ordering::SeqCst);
42 T::regs().cr().modify(|reg| {
43 reg.set_adstart(true);
44 });
45 self.ring_buf.start(); 46 self.ring_buf.start();
46 }
47 47
48 #[inline] 48 Adc::<T>::start();
49 pub fn stop_continuous_sampling(&mut self) {
50 // Stop adc conversion
51 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
52 T::regs().cr().modify(|reg| {
53 reg.set_adstp(true);
54 });
55 while T::regs().cr().read().adstart() {}
56 }
57 }
58 pub fn disable_adc(&mut self) {
59 self.stop_continuous_sampling();
60 self.ring_buf.clear();
61 self.ring_buf.request_pause();
62 } 49 }
63 50
64 pub fn teardown_adc(&mut self) { 51 pub fn stop(&mut self) {
65 self.disable_adc(); 52 Adc::<T>::stop();
66 53
67 //disable dma control 54 self.ring_buf.request_pause();
68 #[cfg(not(any(adc_g0, adc_u0)))]
69 T::regs().cfgr().modify(|reg| {
70 reg.set_dmaen(false);
71 });
72 #[cfg(any(adc_g0, adc_u0))]
73 T::regs().cfgr1().modify(|reg| {
74 reg.set_dmaen(false);
75 });
76
77 //TODO: do we need to cleanup the DMA request here?
78 55
79 compiler_fence(Ordering::SeqCst); 56 compiler_fence(Ordering::SeqCst);
80 } 57 }
81 58
59 pub fn clear(&mut self) {
60 self.ring_buf.clear();
61 }
62
82 /// Reads measurements from the DMA ring buffer. 63 /// Reads measurements from the DMA ring buffer.
83 /// 64 ///
84 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. 65 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
@@ -131,11 +112,18 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
131 "Buffer size must be half the size of the ring buffer" 112 "Buffer size must be half the size of the ring buffer"
132 ); 113 );
133 114
134 let r = T::regs(); 115 if !self.ring_buf.is_running() {
116 self.start();
117 }
135 118
136 // Start background receive if it was not already started 119 #[cfg(adc_v2)]
137 if !r.cr().read().adstart() { 120 {
138 self.start_continuous_sampling(); 121 // Clear overrun flag if set.
122 if T::regs().sr().read().ovr() {
123 self.stop();
124
125 return Err(OverrunError);
126 }
139 } 127 }
140 128
141 self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) 129 self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError)
@@ -150,13 +138,19 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
150 /// Receive in the background is terminated if an error is returned. 138 /// Receive in the background is terminated if an error is returned.
151 /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`. 139 /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`.
152 pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> { 140 pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> {
153 let r = T::regs(); 141 if !self.ring_buf.is_running() {
154 142 self.start();
155 // Start background receive if it was not already started
156 if !r.cr().read().adstart() {
157 self.start_continuous_sampling();
158 } 143 }
159 144
145 #[cfg(adc_v2)]
146 {
147 // Clear overrun flag if set.
148 if T::regs().sr().read().ovr() {
149 self.stop();
150
151 return Err(OverrunError);
152 }
153 }
160 loop { 154 loop {
161 match self.ring_buf.read(buf) { 155 match self.ring_buf.read(buf) {
162 Ok((0, _)) => {} 156 Ok((0, _)) => {}
@@ -164,7 +158,8 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
164 return Ok(len); 158 return Ok(len);
165 } 159 }
166 Err(_) => { 160 Err(_) => {
167 self.stop_continuous_sampling(); 161 self.stop();
162
168 return Err(OverrunError); 163 return Err(OverrunError);
169 } 164 }
170 } 165 }
@@ -174,7 +169,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
174 169
175impl<T: Instance> Drop for RingBufferedAdc<'_, T> { 170impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
176 fn drop(&mut self) { 171 fn drop(&mut self) {
177 self.teardown_adc(); 172 Adc::<T>::teardown_adc();
173
174 compiler_fence(Ordering::SeqCst);
175
176 self.ring_buf.request_pause();
178 rcc::disable::<T>(); 177 rcc::disable::<T>();
179 } 178 }
180} 179}
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs
deleted file mode 100644
index 9b2e5b8fe..000000000
--- a/embassy-stm32/src/adc/ringbuffered_v2.rs
+++ /dev/null
@@ -1,432 +0,0 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::sync::atomic::{Ordering, compiler_fence};
4
5use stm32_metapac::adc::vals::SampleTime;
6
7use crate::adc::{Adc, AdcChannel, Instance, RxDma};
8use crate::dma::{Priority, ReadableRingBuffer, TransferOptions};
9use crate::pac::adc::vals;
10use crate::{Peri, rcc};
11
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct OverrunError;
14
15fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
16 r.sr().modify(|regs| {
17 regs.set_eoc(false);
18 regs.set_ovr(false);
19 });
20}
21
22#[derive(PartialOrd, PartialEq, Debug, Clone, Copy)]
23pub enum Sequence {
24 One,
25 Two,
26 Three,
27 Four,
28 Five,
29 Six,
30 Seven,
31 Eight,
32 Nine,
33 Ten,
34 Eleven,
35 Twelve,
36 Thirteen,
37 Fourteen,
38 Fifteen,
39 Sixteen,
40}
41
42impl From<Sequence> for u8 {
43 fn from(s: Sequence) -> u8 {
44 match s {
45 Sequence::One => 0,
46 Sequence::Two => 1,
47 Sequence::Three => 2,
48 Sequence::Four => 3,
49 Sequence::Five => 4,
50 Sequence::Six => 5,
51 Sequence::Seven => 6,
52 Sequence::Eight => 7,
53 Sequence::Nine => 8,
54 Sequence::Ten => 9,
55 Sequence::Eleven => 10,
56 Sequence::Twelve => 11,
57 Sequence::Thirteen => 12,
58 Sequence::Fourteen => 13,
59 Sequence::Fifteen => 14,
60 Sequence::Sixteen => 15,
61 }
62 }
63}
64
65impl From<u8> for Sequence {
66 fn from(val: u8) -> Self {
67 match val {
68 0 => Sequence::One,
69 1 => Sequence::Two,
70 2 => Sequence::Three,
71 3 => Sequence::Four,
72 4 => Sequence::Five,
73 5 => Sequence::Six,
74 6 => Sequence::Seven,
75 7 => Sequence::Eight,
76 8 => Sequence::Nine,
77 9 => Sequence::Ten,
78 10 => Sequence::Eleven,
79 11 => Sequence::Twelve,
80 12 => Sequence::Thirteen,
81 13 => Sequence::Fourteen,
82 14 => Sequence::Fifteen,
83 15 => Sequence::Sixteen,
84 _ => panic!("Invalid sequence number"),
85 }
86 }
87}
88
89pub struct RingBufferedAdc<'d, T: Instance> {
90 _phantom: PhantomData<T>,
91 ring_buf: ReadableRingBuffer<'d, u16>,
92}
93
94impl<'d, T: Instance> Adc<'d, T> {
95 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
96 ///
97 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
98 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
99 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
100 ///
101 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
102 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
103 ///
104 /// [`read`]: #method.read
105 pub fn into_ring_buffered(self, dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> RingBufferedAdc<'d, T> {
106 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
107
108 let opts: crate::dma::TransferOptions = TransferOptions {
109 half_transfer_ir: true,
110 priority: Priority::VeryHigh,
111 ..Default::default()
112 };
113
114 // Safety: we forget the struct before this function returns.
115 let rx_src = T::regs().dr().as_ptr() as *mut u16;
116 let request = dma.request();
117
118 let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) };
119
120 // Don't disable the clock
121 mem::forget(self);
122
123 RingBufferedAdc {
124 _phantom: PhantomData,
125 ring_buf,
126 }
127 }
128}
129
130impl<'d, T: Instance> RingBufferedAdc<'d, T> {
131 fn is_on() -> bool {
132 T::regs().cr2().read().adon()
133 }
134
135 fn stop_adc() {
136 T::regs().cr2().modify(|reg| {
137 reg.set_adon(false);
138 });
139 }
140
141 fn start_adc() {
142 T::regs().cr2().modify(|reg| {
143 reg.set_adon(true);
144 });
145 }
146
147 /// Sets the channel sample time
148 ///
149 /// ## SAFETY:
150 /// - ADON == 0 i.e ADC must not be enabled when this is called.
151 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
152 if ch <= 9 {
153 T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
154 } else {
155 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
156 }
157 }
158
159 fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) {
160 let ch_iter = ch.iter();
161 for idx in ch_iter {
162 unsafe {
163 Self::set_channel_sample_time(*idx, sample_time);
164 }
165 }
166 }
167
168 pub fn set_sample_sequence(
169 &mut self,
170 sequence: Sequence,
171 channel: &mut impl AdcChannel<T>,
172 sample_time: SampleTime,
173 ) {
174 let was_on = Self::is_on();
175 if !was_on {
176 Self::start_adc();
177 }
178
179 // Check the sequence is long enough
180 T::regs().sqr1().modify(|r| {
181 let prev: Sequence = r.l().into();
182 if prev < sequence {
183 let new_l: Sequence = sequence;
184 trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8);
185 r.set_l(sequence.into())
186 } else {
187 r.set_l(prev.into())
188 }
189 });
190
191 // Set this GPIO as an analog input.
192 channel.setup();
193
194 // Set the channel in the right sequence field.
195 match sequence {
196 Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())),
197 Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())),
198 Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())),
199 Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())),
200 Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())),
201 Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())),
202 Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())),
203 Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())),
204 Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())),
205 Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())),
206 Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())),
207 Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())),
208 Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())),
209 Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())),
210 Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())),
211 Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())),
212 };
213
214 if !was_on {
215 Self::stop_adc();
216 }
217
218 self.set_channels_sample_time(&[channel.channel()], sample_time);
219
220 Self::start_adc();
221 }
222
223 /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.
224 pub fn start(&mut self) -> Result<(), OverrunError> {
225 self.setup_adc();
226 self.ring_buf.clear();
227
228 Ok(())
229 }
230
231 fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> {
232 self.teardown_adc();
233 Err(err)
234 }
235
236 /// Stops DMA transfer.
237 /// It does not turn off ADC.
238 /// Calling `start` restarts continuous DMA transfer.
239 ///
240 /// [`start`]: #method.start
241 pub fn teardown_adc(&mut self) {
242 // Stop the DMA transfer
243 self.ring_buf.request_pause();
244
245 let r = T::regs();
246
247 // Stop ADC
248 r.cr2().modify(|reg| {
249 // Stop ADC
250 reg.set_swstart(false);
251 // Stop DMA
252 reg.set_dma(false);
253 });
254
255 r.cr1().modify(|w| {
256 // Disable interrupt for end of conversion
257 w.set_eocie(false);
258 // Disable interrupt for overrun
259 w.set_ovrie(false);
260 });
261
262 clear_interrupt_flags(r);
263
264 compiler_fence(Ordering::SeqCst);
265 }
266
267 fn setup_adc(&mut self) {
268 compiler_fence(Ordering::SeqCst);
269
270 self.ring_buf.start();
271
272 let r = T::regs();
273
274 // Enable ADC
275 let was_on = Self::is_on();
276 if !was_on {
277 r.cr2().modify(|reg| {
278 reg.set_adon(false);
279 reg.set_swstart(false);
280 });
281 }
282
283 // Clear all interrupts
284 r.sr().modify(|regs| {
285 regs.set_eoc(false);
286 regs.set_ovr(false);
287 regs.set_strt(false);
288 });
289
290 r.cr1().modify(|w| {
291 // Enable interrupt for end of conversion
292 w.set_eocie(true);
293 // Enable interrupt for overrun
294 w.set_ovrie(true);
295 // Scanning converisons of multiple channels
296 w.set_scan(true);
297 // Continuous conversion mode
298 w.set_discen(false);
299 });
300
301 r.cr2().modify(|w| {
302 // Enable DMA mode
303 w.set_dma(true);
304 // Enable continuous conversions
305 w.set_cont(true);
306 // DMA requests are issues as long as DMA=1 and data are converted.
307 w.set_dds(vals::Dds::CONTINUOUS);
308 // EOC flag is set at the end of each conversion.
309 w.set_eocs(vals::Eocs::EACH_CONVERSION);
310 });
311
312 // Begin ADC conversions
313 T::regs().cr2().modify(|reg| {
314 reg.set_adon(true);
315 reg.set_swstart(true);
316 });
317
318 super::blocking_delay_us(3);
319 }
320
321 /// Read bytes that are readily available in the ring buffer.
322 /// If no bytes are currently available in the buffer the call waits until the some
323 /// bytes are available (at least one byte and at most half the buffer size)
324 ///
325 /// Background receive is started if `start()` has not been previously called.
326 ///
327 /// Receive in the background is terminated if an error is returned.
328 /// It must then manually be started again by calling `start()` or by re-calling `read()`.
329 pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
330 let r = T::regs();
331
332 // Start background receive if it was not already started
333 if !r.cr2().read().dma() {
334 self.start()?;
335 }
336
337 // Clear overrun flag if set.
338 if r.sr().read().ovr() {
339 return self.stop(OverrunError);
340 }
341
342 loop {
343 match self.ring_buf.read(buf) {
344 Ok((0, _)) => {}
345 Ok((len, _)) => {
346 return Ok(len);
347 }
348 Err(_) => {
349 return self.stop(OverrunError);
350 }
351 }
352 }
353 }
354
355 /// Reads measurements from the DMA ring buffer.
356 ///
357 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
358 /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
359 ///
360 /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`.
361 /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
362 /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`.
363 ///
364 /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again.
365 ///
366 /// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval.
367 /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected.
368 ///
369 /// Example:
370 /// ```rust,ignore
371 /// const DMA_BUF_LEN: usize = 120;
372 /// let adc_dma_buf = [0u16; DMA_BUF_LEN];
373 /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf);
374 ///
375 /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
376 /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112);
377 /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112);
378 ///
379 /// let mut measurements = [0u16; DMA_BUF_LEN / 2];
380 /// loop {
381 /// match adc.read(&mut measurements).await {
382 /// Ok(_) => {
383 /// defmt::info!("adc1: {}", measurements);
384 /// // Only needed to manually control sample rate.
385 /// adc.teardown_adc();
386 /// }
387 /// Err(e) => {
388 /// defmt::warn!("Error: {:?}", e);
389 /// // DMA overrun, next call to `read` restarts ADC.
390 /// }
391 /// }
392 ///
393 /// // Manually control sample rate.
394 /// Timer::after_millis(100).await;
395 /// }
396 /// ```
397 ///
398 ///
399 /// [`set_sample_sequence`]: #method.set_sample_sequence
400 /// [`teardown_adc`]: #method.teardown_adc
401 /// [`start`]: #method.start
402 pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> {
403 assert_eq!(
404 self.ring_buf.capacity() / 2,
405 N,
406 "Buffer size must be half the size of the ring buffer"
407 );
408
409 let r = T::regs();
410
411 // Start background receive if it was not already started
412 if !r.cr2().read().dma() {
413 self.start()?;
414 }
415
416 // Clear overrun flag if set.
417 if r.sr().read().ovr() {
418 return self.stop(OverrunError);
419 }
420 match self.ring_buf.read_exact(measurements).await {
421 Ok(len) => Ok(len),
422 Err(_) => self.stop(OverrunError),
423 }
424 }
425}
426
427impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
428 fn drop(&mut self) {
429 self.teardown_adc();
430 rcc::disable::<T>();
431 }
432}
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 93ec78548..90c6294d2 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -1,11 +1,22 @@
1use core::mem;
2use core::sync::atomic::{Ordering, compiler_fence};
3
1use super::blocking_delay_us; 4use super::blocking_delay_us;
2use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; 5use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel};
6use crate::pac::adc::vals;
3use crate::peripherals::ADC1; 7use crate::peripherals::ADC1;
4use crate::time::Hertz; 8use crate::time::Hertz;
5use crate::{Peri, rcc}; 9use crate::{Peri, rcc};
6 10
7mod ringbuffered_v2; 11mod ringbuffered;
8pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; 12pub use ringbuffered::RingBufferedAdc;
13
14fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
15 r.sr().modify(|regs| {
16 regs.set_eoc(false);
17 regs.set_ovr(false);
18 });
19}
9 20
10/// Default VREF voltage used for sample conversion to millivolts. 21/// Default VREF voltage used for sample conversion to millivolts.
11pub const VREF_DEFAULT_MV: u32 = 3300; 22pub const VREF_DEFAULT_MV: u32 = 3300;
@@ -112,6 +123,98 @@ where
112 } 123 }
113 } 124 }
114 125
126 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
127 ///
128 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
129 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
130 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
131 ///
132 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
133 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
134 ///
135 /// [`read`]: #method.read
136 pub fn into_ring_buffered<'a>(
137 self,
138 dma: Peri<'d, impl RxDma<T>>,
139 dma_buf: &'d mut [u16],
140 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
141 ) -> RingBufferedAdc<'d, T> {
142 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
143
144 T::regs().cr2().modify(|reg| {
145 reg.set_adon(true);
146 });
147
148 // Check the sequence is long enough
149 T::regs().sqr1().modify(|r| {
150 r.set_l((sequence.len() - 1).try_into().unwrap());
151 });
152
153 for (i, (channel, sample_time)) in sequence.enumerate() {
154 // Set this GPIO as an analog input.
155 channel.setup();
156
157 // Set the channel in the right sequence field.
158 T::regs().sqr3().modify(|w| w.set_sq(i, channel.channel()));
159
160 Self::set_channel_sample_time(channel.channel(), sample_time);
161 }
162
163 compiler_fence(Ordering::SeqCst);
164
165 let r = T::regs();
166
167 // Clear all interrupts
168 r.sr().modify(|regs| {
169 regs.set_eoc(false);
170 regs.set_ovr(false);
171 regs.set_strt(false);
172 });
173
174 r.cr1().modify(|w| {
175 // Enable interrupt for end of conversion
176 w.set_eocie(true);
177 // Enable interrupt for overrun
178 w.set_ovrie(true);
179 // Scanning converisons of multiple channels
180 w.set_scan(true);
181 // Continuous conversion mode
182 w.set_discen(false);
183 });
184
185 r.cr2().modify(|w| {
186 // Enable DMA mode
187 w.set_dma(true);
188 // Enable continuous conversions
189 w.set_cont(true);
190 // DMA requests are issues as long as DMA=1 and data are converted.
191 w.set_dds(vals::Dds::CONTINUOUS);
192 // EOC flag is set at the end of each conversion.
193 w.set_eocs(vals::Eocs::EACH_CONVERSION);
194 });
195
196 // Don't disable the clock
197 mem::forget(self);
198
199 RingBufferedAdc::new(dma, dma_buf)
200 }
201
202 pub(super) fn start() {
203 // Begin ADC conversions
204 T::regs().cr2().modify(|reg| {
205 reg.set_adon(true);
206 reg.set_swstart(true);
207 });
208 }
209
210 pub(super) fn stop() {
211 // Stop ADC
212 T::regs().cr2().modify(|reg| {
213 // Stop ADC
214 reg.set_swstart(false);
215 });
216 }
217
115 pub fn set_sample_time(&mut self, sample_time: SampleTime) { 218 pub fn set_sample_time(&mut self, sample_time: SampleTime) {
116 self.sample_time = sample_time; 219 self.sample_time = sample_time;
117 } 220 }
@@ -198,6 +301,31 @@ where
198 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); 301 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
199 } 302 }
200 } 303 }
304
305 pub(super) fn teardown_adc() {
306 let r = T::regs();
307
308 // Stop ADC
309 r.cr2().modify(|reg| {
310 // Stop ADC
311 reg.set_swstart(false);
312 // Stop ADC
313 reg.set_adon(false);
314 // Stop DMA
315 reg.set_dma(false);
316 });
317
318 r.cr1().modify(|w| {
319 // Disable interrupt for end of conversion
320 w.set_eocie(false);
321 // Disable interrupt for overrun
322 w.set_ovrie(false);
323 });
324
325 clear_interrupt_flags(r);
326
327 compiler_fence(Ordering::SeqCst);
328 }
201} 329}
202 330
203impl<'d, T: Instance> Drop for Adc<'d, T> { 331impl<'d, T: Instance> Drop for Adc<'d, T> {
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index e5b9a5d85..62c2da557 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -14,10 +14,10 @@ use super::{
14}; 14};
15 15
16#[cfg(any(adc_v3, adc_g0, adc_u0))] 16#[cfg(any(adc_v3, adc_g0, adc_u0))]
17mod ringbuffered_v3; 17mod ringbuffered;
18 18
19#[cfg(any(adc_v3, adc_g0, adc_u0))] 19#[cfg(any(adc_v3, adc_g0, adc_u0))]
20use ringbuffered_v3::RingBufferedAdc; 20use ringbuffered::RingBufferedAdc;
21 21
22use crate::dma::Transfer; 22use crate::dma::Transfer;
23use crate::{Peri, pac, rcc}; 23use crate::{Peri, pac, rcc};
@@ -181,6 +181,38 @@ impl<'d, T: Instance> Adc<'d, T> {
181 blocking_delay_us(1); 181 blocking_delay_us(1);
182 } 182 }
183 183
184 #[cfg(any(adc_v3, adc_g0, adc_u0))]
185 pub(super) fn start() {
186 // Start adc conversion
187 T::regs().cr().modify(|reg| {
188 reg.set_adstart(true);
189 });
190 }
191
192 #[cfg(any(adc_v3, adc_g0, adc_u0))]
193 pub(super) fn stop() {
194 // Stop adc conversion
195 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
196 T::regs().cr().modify(|reg| {
197 reg.set_adstp(true);
198 });
199 while T::regs().cr().read().adstart() {}
200 }
201 }
202
203 #[cfg(any(adc_v3, adc_g0, adc_u0))]
204 pub(super) fn teardown_adc() {
205 //disable dma control
206 #[cfg(not(any(adc_g0, adc_u0)))]
207 T::regs().cfgr().modify(|reg| {
208 reg.set_dmaen(false);
209 });
210 #[cfg(any(adc_g0, adc_u0))]
211 T::regs().cfgr1().modify(|reg| {
212 reg.set_dmaen(false);
213 });
214 }
215
184 /// Initialize the ADC leaving any analog clock at reset value. 216 /// Initialize the ADC leaving any analog clock at reset value.
185 /// For G0 and WL, this is the async clock without prescaler. 217 /// For G0 and WL, this is the async clock without prescaler.
186 pub fn new(adc: Peri<'d, T>) -> Self { 218 pub fn new(adc: Peri<'d, T>) -> Self {
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
index c24f01753..f8da91336 100644
--- a/examples/stm32f4/src/bin/adc_dma.rs
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -4,7 +4,7 @@ use cortex_m::singleton;
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::Peripherals; 6use embassy_stm32::Peripherals;
7use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; 7use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime};
8use embassy_time::Instant; 8use embassy_time::Instant;
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
10 10
@@ -15,7 +15,7 @@ async fn main(spawner: Spawner) {
15} 15}
16 16
17#[embassy_executor::task] 17#[embassy_executor::task]
18async fn adc_task(mut p: Peripherals) { 18async fn adc_task(p: Peripherals) {
19 const ADC_BUF_SIZE: usize = 1024; 19 const ADC_BUF_SIZE: usize = 1024;
20 let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); 20 let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
21 let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); 21 let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
@@ -23,13 +23,24 @@ async fn adc_task(mut p: Peripherals) {
23 let adc = Adc::new(p.ADC1); 23 let adc = Adc::new(p.ADC1);
24 let adc2 = Adc::new(p.ADC2); 24 let adc2 = Adc::new(p.ADC2);
25 25
26 let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data); 26 let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(
27 let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(p.DMA2_CH2, adc_data2); 27 p.DMA2_CH0,
28 28 adc_data,
29 adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); 29 [
30 adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112); 30 (&mut p.PA0.degrade_adc(), SampleTime::CYCLES112),
31 adc2.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112); 31 (&mut p.PA2.degrade_adc(), SampleTime::CYCLES112),
32 adc2.set_sample_sequence(Sequence::Two, &mut p.PA3, SampleTime::CYCLES112); 32 ]
33 .into_iter(),
34 );
35 let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(
36 p.DMA2_CH2,
37 adc_data2,
38 [
39 (&mut p.PA1.degrade_adc(), SampleTime::CYCLES112),
40 (&mut p.PA3.degrade_adc(), SampleTime::CYCLES112),
41 ]
42 .into_iter(),
43 );
33 44
34 // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around 45 // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around
35 // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of 46 // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of