diff options
| author | Andres Vahter <[email protected]> | 2024-07-02 15:22:50 +0300 |
|---|---|---|
| committer | Andres Vahter <[email protected]> | 2024-07-02 16:53:49 +0300 |
| commit | b88e1a5d7180871d7e8564a3060b50e166602902 (patch) | |
| tree | 7bf1627e8779df21b73571adee70df445ead6baf /embassy-stm32 | |
| parent | 6f21d5e478292352df5cabcc1aa302ae7f8998a3 (diff) | |
stm32 ringbuffered adc docs
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/src/adc/ringbuffered_v2.rs | 70 |
1 files changed, 68 insertions, 2 deletions
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index fb29d9a8c..82b67c533 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs | |||
| @@ -91,6 +91,15 @@ pub struct RingBufferedAdc<'d, T: Instance> { | |||
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | impl<'d, T: Instance> Adc<'d, T> { | 93 | impl<'d, T: Instance> Adc<'d, T> { |
| 94 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 95 | /// | ||
| 96 | /// The `dma_buf` should be large enough to prevent buffer overflow, allowing sufficient time to read out measurements. | ||
| 97 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 98 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 99 | /// | ||
| 100 | /// `read_exact` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 101 | /// | ||
| 102 | /// [`read_exact`]: #method.read_exact | ||
| 94 | pub fn into_ring_buffered( | 103 | pub fn into_ring_buffered( |
| 95 | self, | 104 | self, |
| 96 | dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 105 | dma: impl Peripheral<P = impl RxDma<T>> + 'd, |
| @@ -214,6 +223,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 214 | Self::start_adc(); | 223 | Self::start_adc(); |
| 215 | } | 224 | } |
| 216 | 225 | ||
| 226 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 217 | pub fn start(&mut self) -> Result<(), OverrunError> { | 227 | pub fn start(&mut self) -> Result<(), OverrunError> { |
| 218 | self.ring_buf.clear(); | 228 | self.ring_buf.clear(); |
| 219 | 229 | ||
| @@ -227,6 +237,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 227 | Err(err) | 237 | Err(err) |
| 228 | } | 238 | } |
| 229 | 239 | ||
| 240 | /// Stops DMA transfer. | ||
| 241 | /// It does not turn off ADC. | ||
| 242 | /// Calling `start` restarts continuous DMA transfer. | ||
| 243 | /// | ||
| 244 | /// [`start`]: #method.start | ||
| 230 | pub fn teardown_adc(&mut self) { | 245 | pub fn teardown_adc(&mut self) { |
| 231 | // Stop the DMA transfer | 246 | // Stop the DMA transfer |
| 232 | self.ring_buf.request_stop(); | 247 | self.ring_buf.request_stop(); |
| @@ -341,7 +356,58 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 341 | } | 356 | } |
| 342 | } | 357 | } |
| 343 | 358 | ||
| 344 | pub async fn read_exact<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> { | 359 | /// Reads measurements from the DMA ring buffer. |
| 360 | /// | ||
| 361 | /// This method fills the provided `measurements` array with ADC readings. | ||
| 362 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. | ||
| 363 | /// Because interrupts are only generated if half or full DMA transfer completes. | ||
| 364 | /// | ||
| 365 | /// Each call to `read_exact` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`. | ||
| 366 | /// 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. | ||
| 367 | /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`. | ||
| 368 | /// | ||
| 369 | /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` again. | ||
| 370 | /// | ||
| 371 | /// 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. | ||
| 372 | /// Note that even if using `teardown_adc` to control sample rate, with each call to `read_exact`, measurements equivalent to half the size of the DMA buffer are still collected. | ||
| 373 | /// | ||
| 374 | /// Example: | ||
| 375 | /// ```rust,ignore | ||
| 376 | /// const DMA_BUF_LEN: usize = 120; | ||
| 377 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 378 | /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf); | ||
| 379 | /// | ||
| 380 | /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); | ||
| 381 | /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112); | ||
| 382 | /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112); | ||
| 383 | /// | ||
| 384 | /// adc.start.unwrap(); | ||
| 385 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 386 | /// loop { | ||
| 387 | /// match adc.read_exact(&mut measurements).await { | ||
| 388 | /// Ok(_) => { | ||
| 389 | /// defmt::info!("adc1: {}", measurements); | ||
| 390 | /// // Only needed to manually control sample rate. | ||
| 391 | /// adc.teardown_adc(); | ||
| 392 | /// } | ||
| 393 | /// Err(e) => { | ||
| 394 | /// defmt::warn!("Error: {:?}", e); | ||
| 395 | /// // DMA overflow, restart ADC. | ||
| 396 | /// let _ = adc.start(); | ||
| 397 | /// } | ||
| 398 | /// } | ||
| 399 | /// | ||
| 400 | /// // Manually control sample rate. | ||
| 401 | /// Timer::after_millis(100).await; | ||
| 402 | /// let _ = adc.start(); | ||
| 403 | /// } | ||
| 404 | /// ``` | ||
| 405 | /// | ||
| 406 | /// | ||
| 407 | /// [`set_sample_sequence`]: #method.set_sample_sequence | ||
| 408 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 409 | /// [`start`]: #method.start | ||
| 410 | pub async fn read_exact<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 345 | let r = T::regs(); | 411 | let r = T::regs(); |
| 346 | 412 | ||
| 347 | // Start background receive if it was not already started | 413 | // Start background receive if it was not already started |
| @@ -353,7 +419,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 353 | if r.sr().read().ovr() { | 419 | if r.sr().read().ovr() { |
| 354 | return self.stop(OverrunError); | 420 | return self.stop(OverrunError); |
| 355 | } | 421 | } |
| 356 | match self.ring_buf.read_exact(buf).await { | 422 | match self.ring_buf.read_exact(measurements).await { |
| 357 | Ok(len) => Ok(len), | 423 | Ok(len) => Ok(len), |
| 358 | Err(_) => self.stop(OverrunError), | 424 | Err(_) => self.stop(OverrunError), |
| 359 | } | 425 | } |
