From b88e1a5d7180871d7e8564a3060b50e166602902 Mon Sep 17 00:00:00 2001 From: Andres Vahter Date: Tue, 2 Jul 2024 15:22:50 +0300 Subject: [PATCH 1/3] stm32 ringbuffered adc docs --- embassy-stm32/src/adc/ringbuffered_v2.rs | 70 +++++++++++++++++++++++- 1 file 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> { } impl<'d, T: Instance> Adc<'d, T> { + /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. + /// + /// The `dma_buf` should be large enough to prevent buffer overflow, allowing sufficient time to read out measurements. + /// The length of the `dma_buf` should be a multiple of the ADC channel count. + /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. + /// + /// `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. + /// + /// [`read_exact`]: #method.read_exact pub fn into_ring_buffered( self, dma: impl Peripheral

> + 'd, @@ -214,6 +223,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { Self::start_adc(); } + /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. pub fn start(&mut self) -> Result<(), OverrunError> { self.ring_buf.clear(); @@ -227,6 +237,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { Err(err) } + /// Stops DMA transfer. + /// It does not turn off ADC. + /// Calling `start` restarts continuous DMA transfer. + /// + /// [`start`]: #method.start pub fn teardown_adc(&mut self) { // Stop the DMA transfer self.ring_buf.request_stop(); @@ -341,7 +356,58 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } } - pub async fn read_exact(&mut self, buf: &mut [u16; N]) -> Result { + /// Reads measurements from the DMA ring buffer. + /// + /// This method fills the provided `measurements` array with ADC readings. + /// 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. + /// + /// Each call to `read_exact` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`. + /// 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. + /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`. + /// + /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` again. + /// + /// 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. + /// 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. + /// + /// Example: + /// ```rust,ignore + /// const DMA_BUF_LEN: usize = 120; + /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; + /// let mut adc: RingBufferedAdc = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf); + /// + /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); + /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112); + /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112); + /// + /// adc.start.unwrap(); + /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; + /// loop { + /// match adc.read_exact(&mut measurements).await { + /// Ok(_) => { + /// defmt::info!("adc1: {}", measurements); + /// // Only needed to manually control sample rate. + /// adc.teardown_adc(); + /// } + /// Err(e) => { + /// defmt::warn!("Error: {:?}", e); + /// // DMA overflow, restart ADC. + /// let _ = adc.start(); + /// } + /// } + /// + /// // Manually control sample rate. + /// Timer::after_millis(100).await; + /// let _ = adc.start(); + /// } + /// ``` + /// + /// + /// [`set_sample_sequence`]: #method.set_sample_sequence + /// [`teardown_adc`]: #method.teardown_adc + /// [`start`]: #method.start + pub async fn read_exact(&mut self, measurements: &mut [u16; N]) -> Result { let r = T::regs(); // Start background receive if it was not already started @@ -353,7 +419,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { if r.sr().read().ovr() { return self.stop(OverrunError); } - match self.ring_buf.read_exact(buf).await { + match self.ring_buf.read_exact(measurements).await { Ok(len) => Ok(len), Err(_) => self.stop(OverrunError), } From dd69efe70804f57c17adb45602798dad617cb41d Mon Sep 17 00:00:00 2001 From: Andres Vahter Date: Tue, 2 Jul 2024 16:56:19 +0300 Subject: [PATCH 2/3] stm32 ringbuffered adc: add buf size assert --- embassy-stm32/src/adc/ringbuffered_v2.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 82b67c533..3f5277a9d 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -408,6 +408,12 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// [`teardown_adc`]: #method.teardown_adc /// [`start`]: #method.start pub async fn read_exact(&mut self, measurements: &mut [u16; N]) -> Result { + assert_eq!( + self.ring_buf.capacity() / 2, + N, + "Buffer size must be half the size of the ring buffer" + ); + let r = T::regs(); // Start background receive if it was not already started From d4ff7616f952bc801390e46bb3163b63cc01828d Mon Sep 17 00:00:00 2001 From: Andres Vahter Date: Tue, 2 Jul 2024 17:35:15 +0300 Subject: [PATCH 3/3] stm32 ringbuffered adc docs improvements --- embassy-stm32/src/adc/ringbuffered_v2.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 3f5277a9d..8df224783 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -93,11 +93,12 @@ pub struct RingBufferedAdc<'d, T: Instance> { impl<'d, T: Instance> Adc<'d, T> { /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. /// - /// The `dma_buf` should be large enough to prevent buffer overflow, allowing sufficient time to read out measurements. + /// The `dma_buf` should be large enough to prevent DMA buffer overrun. /// The length of the `dma_buf` should be a multiple of the ADC channel count. /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. /// /// `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. + /// It is critical to call `read_exact` frequently to prevent DMA buffer overrun. /// /// [`read_exact`]: #method.read_exact pub fn into_ring_buffered( @@ -358,18 +359,17 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// Reads measurements from the DMA ring buffer. /// - /// This method fills the provided `measurements` array with ADC readings. - /// 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. + /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. + /// 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. /// /// Each call to `read_exact` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`. /// 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. /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`. /// - /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` again. + /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read_exact` again. /// /// 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. - /// 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. + /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read_exact`, measurements equivalent to half the size of the DMA buffer are still collected. /// /// Example: /// ```rust,ignore @@ -381,7 +381,6 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112); /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112); /// - /// adc.start.unwrap(); /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; /// loop { /// match adc.read_exact(&mut measurements).await { @@ -392,14 +391,12 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// } /// Err(e) => { /// defmt::warn!("Error: {:?}", e); - /// // DMA overflow, restart ADC. - /// let _ = adc.start(); + /// // DMA overrun, next call to `read_exact` restart ADC. /// } /// } /// /// // Manually control sample rate. /// Timer::after_millis(100).await; - /// let _ = adc.start(); /// } /// ``` ///