From f64dd8228b7b8a570546ffa9b522ae85145cfdef Mon Sep 17 00:00:00 2001
From: seth <seth@transastra.com>
Date: Mon, 24 Jun 2024 17:09:43 -0700
Subject: [PATCH 1/5] new PR, taking Dirbao's advice to make the DMA impl in a
 separate struct that consumes Adc<T> to make RingBufferedAdc<T>. Handling
 overrun similar to RingBufferedUart

---
 embassy-stm32/build.rs                   |   6 +-
 embassy-stm32/src/adc/mod.rs             |   2 +
 embassy-stm32/src/adc/ringbuffered_v2.rs | 376 +++++++++++++++++++++++
 embassy-stm32/src/adc/v2.rs              |   3 +
 examples/stm32f4/src/bin/adc_dma.rs      |  59 ++++
 5 files changed, 445 insertions(+), 1 deletion(-)
 create mode 100644 embassy-stm32/src/adc/ringbuffered_v2.rs
 create mode 100644 examples/stm32f4/src/bin/adc_dma.rs

diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 6aedcc228..24e2226a2 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -764,7 +764,7 @@ fn main() {
 
     #[rustfmt::skip]
     let signals: HashMap<_, _> = [
-                // (kind, signal) => trait
+        // (kind, signal) => trait
         (("ucpd", "CC1"), quote!(crate::ucpd::Cc1Pin)),
         (("ucpd", "CC2"), quote!(crate::ucpd::Cc2Pin)),
         (("usart", "TX"), quote!(crate::usart::TxPin)),
@@ -1178,6 +1178,10 @@ fn main() {
 
     let signals: HashMap<_, _> = [
         // (kind, signal) => trait
+        (("adc", "ADC"), quote!(crate::adc::RxDma)),
+        (("adc", "ADC1"), quote!(crate::adc::RxDma)),
+        (("adc", "ADC2"), quote!(crate::adc::RxDma)),
+        (("adc", "ADC3"), quote!(crate::adc::RxDma)),
         (("ucpd", "RX"), quote!(crate::ucpd::RxDma)),
         (("ucpd", "TX"), quote!(crate::ucpd::TxDma)),
         (("usart", "RX"), quote!(crate::usart::RxDma)),
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 0c22a7dae..7a7d7cd8e 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -28,6 +28,8 @@ pub use crate::pac::adc::vals::Res as Resolution;
 pub use crate::pac::adc::vals::SampleTime;
 use crate::peripherals;
 
+dma_trait!(RxDma, Instance);
+
 /// Analog to Digital driver.
 pub struct Adc<'d, T: Instance> {
     #[allow(unused)]
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs
new file mode 100644
index 000000000..e11311b9a
--- /dev/null
+++ b/embassy-stm32/src/adc/ringbuffered_v2.rs
@@ -0,0 +1,376 @@
+use core::marker::PhantomData;
+use core::mem;
+use core::sync::atomic::{compiler_fence, Ordering};
+
+use embassy_hal_internal::{into_ref, Peripheral};
+use stm32_metapac::adc::vals::SampleTime;
+
+use crate::adc::{Adc, AdcChannel, Instance, RxDma};
+use crate::dma::ringbuffer::OverrunError;
+use crate::dma::{Priority, ReadableRingBuffer, TransferOptions};
+use crate::pac::adc::vals;
+use crate::rcc;
+
+fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
+    r.sr().modify(|regs| {
+        regs.set_eoc(false);
+        regs.set_ovr(false);
+    });
+}
+
+#[derive(PartialOrd, PartialEq, Debug, Clone, Copy)]
+pub enum Sequence {
+    One,
+    Two,
+    Three,
+    Four,
+    Five,
+    Six,
+    Seven,
+    Eight,
+    Nine,
+    Ten,
+    Eleven,
+    Twelve,
+    Thirteen,
+    Fourteen,
+    Fifteen,
+    Sixteen,
+}
+
+impl From<Sequence> for u8 {
+    fn from(s: Sequence) -> u8 {
+        match s {
+            Sequence::One => 0,
+            Sequence::Two => 1,
+            Sequence::Three => 2,
+            Sequence::Four => 3,
+            Sequence::Five => 4,
+            Sequence::Six => 5,
+            Sequence::Seven => 6,
+            Sequence::Eight => 7,
+            Sequence::Nine => 8,
+            Sequence::Ten => 9,
+            Sequence::Eleven => 10,
+            Sequence::Twelve => 11,
+            Sequence::Thirteen => 12,
+            Sequence::Fourteen => 13,
+            Sequence::Fifteen => 14,
+            Sequence::Sixteen => 15,
+        }
+    }
+}
+
+impl From<u8> for Sequence {
+    fn from(val: u8) -> Self {
+        match val {
+            0 => Sequence::One,
+            1 => Sequence::Two,
+            2 => Sequence::Three,
+            3 => Sequence::Four,
+            4 => Sequence::Five,
+            5 => Sequence::Six,
+            6 => Sequence::Seven,
+            7 => Sequence::Eight,
+            8 => Sequence::Nine,
+            9 => Sequence::Ten,
+            10 => Sequence::Eleven,
+            11 => Sequence::Twelve,
+            12 => Sequence::Thirteen,
+            13 => Sequence::Fourteen,
+            14 => Sequence::Fifteen,
+            15 => Sequence::Sixteen,
+            _ => panic!("Invalid sequence number"),
+        }
+    }
+}
+
+pub struct RingBufferedAdc<'d, T: Instance> {
+    _phantom: PhantomData<T>,
+    ring_buf: ReadableRingBuffer<'d, u16>,
+}
+
+impl<'d, T: Instance> Adc<'d, T> {
+    pub fn into_ring_buffered(
+        self,
+        dma: impl Peripheral<P = impl RxDma<T>> + 'd,
+        dma_buf: &'d mut [u16],
+    ) -> RingBufferedAdc<'d, T> {
+        assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
+        into_ref!(dma);
+
+        let opts: crate::dma::TransferOptions = TransferOptions {
+            half_transfer_ir: true,
+            priority: Priority::VeryHigh,
+            ..Default::default()
+        };
+
+        // Safety: we forget the struct before this function returns.
+        let rx_src = T::regs().dr().as_ptr() as *mut u16;
+        let request = dma.request();
+
+        let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) };
+
+        // Don't disable the clock
+        mem::forget(self);
+
+        RingBufferedAdc {
+            _phantom: PhantomData,
+            ring_buf,
+        }
+    }
+}
+
+impl<'d, T: Instance> RingBufferedAdc<'d, T> {
+    fn is_on() -> bool {
+        T::regs().cr2().read().adon()
+    }
+
+    fn stop_adc() {
+        T::regs().cr2().modify(|reg| {
+            reg.set_adon(false);
+        });
+    }
+
+    fn start_adc() {
+        T::regs().cr2().modify(|reg| {
+            reg.set_adon(true);
+        });
+    }
+
+    /// Sets the channel sample time
+    ///
+    /// ## SAFETY:
+    /// - ADON == 0 i.e ADC must not be enabled when this is called.
+    unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+        if ch <= 9 {
+            T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
+        } else {
+            T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
+        }
+    }
+
+    fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) {
+        let ch_iter = ch.iter();
+        for idx in ch_iter {
+            unsafe {
+                Self::set_channel_sample_time(*idx, sample_time);
+            }
+        }
+    }
+
+    pub fn set_sample_sequence(
+        &mut self,
+        sequence: Sequence,
+        channel: &mut impl AdcChannel<T>,
+        sample_time: SampleTime,
+    ) {
+        let was_on = Self::is_on();
+        if !was_on {
+            Self::start_adc();
+        }
+
+        //Check the sequence is long enough
+        T::regs().sqr1().modify(|r| {
+            let prev: Sequence = r.l().into();
+            if prev < sequence {
+                let new_l: Sequence = sequence;
+                trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8);
+                r.set_l(sequence.into())
+            } else {
+                r.set_l(prev.into())
+            }
+        });
+
+        //Set this GPIO as an analog input.
+        channel.setup();
+
+        //Set the channel in the right sequence field.
+        match sequence {
+            Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())),
+            Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())),
+            Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())),
+            Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())),
+            Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())),
+            Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())),
+            Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(6, channel.channel())),
+            Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(7, channel.channel())),
+            Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(8, channel.channel())),
+            Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(9, channel.channel())),
+            Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(10, channel.channel())),
+            Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(11, channel.channel())),
+            Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(12, channel.channel())),
+            Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(13, channel.channel())),
+            Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(14, channel.channel())),
+            Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(15, channel.channel())),
+        };
+
+        if !was_on {
+            Self::stop_adc();
+        }
+
+        self.set_channels_sample_time(&[channel.channel()], sample_time);
+
+        Self::start_adc();
+    }
+
+    pub fn start(&mut self) -> Result<(), OverrunError> {
+        self.ring_buf.clear();
+
+        self.setup_adc();
+
+        Ok(())
+    }
+
+    fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> {
+        self.teardown_adc();
+        Err(err)
+    }
+
+    pub fn teardown_adc(&mut self) {
+        // Stop the DMA transfer
+        self.ring_buf.request_stop();
+
+        let r = T::regs();
+
+        // Stop ADC
+        r.cr2().modify(|reg| {
+            // Stop ADC
+            reg.set_swstart(false);
+            // Stop DMA
+            reg.set_dma(false);
+        });
+
+        r.cr1().modify(|w| {
+            // Disable interrupt for end of conversion
+            w.set_eocie(false);
+            // Disable interrupt for overrun
+            w.set_ovrie(false);
+        });
+
+        clear_interrupt_flags(r);
+
+        compiler_fence(Ordering::SeqCst);
+    }
+
+    fn setup_adc(&mut self) {
+        compiler_fence(Ordering::SeqCst);
+
+        self.ring_buf.start();
+
+        let r = T::regs();
+
+        //Enable ADC
+        let was_on = Self::is_on();
+        if !was_on {
+            r.cr2().modify(|reg| {
+                reg.set_adon(false);
+                reg.set_swstart(false);
+            });
+        }
+
+        // Clear all interrupts
+        r.sr().modify(|regs| {
+            regs.set_eoc(false);
+            regs.set_ovr(false);
+            regs.set_strt(false);
+        });
+
+        r.cr1().modify(|w| {
+            // Enable interrupt for end of conversion
+            w.set_eocie(true);
+            // Enable interrupt for overrun
+            w.set_ovrie(true);
+            // Scanning converisons of multiple channels
+            w.set_scan(true);
+            // Continuous conversion mode
+            w.set_discen(false);
+        });
+
+        r.cr2().modify(|w| {
+            // Enable DMA mode
+            w.set_dma(true);
+            // Enable continuous conversions
+            w.set_cont(vals::Cont::CONTINUOUS);
+            // DMA requests are issues as long as DMA=1 and data are converted.
+            w.set_dds(vals::Dds::CONTINUOUS);
+            // EOC flag is set at the end of each conversion.
+            w.set_eocs(vals::Eocs::EACHCONVERSION);
+        });
+
+        //Being ADC conversions
+        T::regs().cr2().modify(|reg| {
+            reg.set_adon(true);
+            reg.set_swstart(true);
+        });
+
+        super::blocking_delay_us(3);
+    }
+
+    /// Read bytes that are readily available in the ring buffer.
+    /// If no bytes are currently available in the buffer the call waits until the some
+    /// bytes are available (at least one byte and at most half the buffer size)
+    ///
+    /// Background receive is started if `start()` has not been previously called.
+    ///
+    /// Receive in the background is terminated if an error is returned.
+    /// It must then manually be started again by calling `start()` or by re-calling `read()`.
+    pub async fn read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
+        let r = T::regs();
+
+        // Start background receive if it was not already started
+        if !r.cr2().read().dma() {
+            self.start()?;
+        }
+
+        // Clear overrun flag if set.
+        if r.sr().read().ovr() {
+            r.sr().modify(|regs| {
+                regs.set_ovr(false);
+                // regs.set_eoc(false);
+            });
+            // return self.stop(OverrunError);
+        }
+
+        loop {
+            match self.ring_buf.read(buf) {
+                Ok((0, _)) => {}
+                Ok((len, _)) => {
+                    return Ok(len);
+                }
+                Err(_) => {
+                    return self.stop(OverrunError);
+                }
+            }
+        }
+    }
+
+    pub async fn read_exact<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
+        let r = T::regs();
+
+        // Start background receive if it was not already started
+        if !r.cr2().read().dma() {
+            self.start()?;
+        }
+
+        // Clear overrun flag if set.
+        if r.sr().read().ovr() {
+            r.sr().modify(|regs| {
+                regs.set_ovr(false);
+                // regs.set_eoc(false);
+            });
+            // return self.stop(OverrunError);
+        }
+        match self.ring_buf.read_exact(buf).await {
+            Ok(len) => Ok(len),
+            Err(_) => self.stop(OverrunError),
+        }
+    }
+}
+
+impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
+    fn drop(&mut self) {
+        self.teardown_adc();
+        rcc::disable::<T>();
+    }
+}
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index e3175dff5..ddeb7ea79 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -6,6 +6,9 @@ use crate::peripherals::ADC1;
 use crate::time::Hertz;
 use crate::{rcc, Peripheral};
 
+mod ringbuffered_v2;
+pub use ringbuffered_v2::{RingBufferedAdc, Sequence};
+
 /// Default VREF voltage used for sample conversion to millivolts.
 pub const VREF_DEFAULT_MV: u32 = 3300;
 /// VREF voltage used for factory calibration of VREFINTCAL register.
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
new file mode 100644
index 000000000..a2611bb6f
--- /dev/null
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -0,0 +1,59 @@
+#![no_std]
+#![no_main]
+use cortex_m::singleton;
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::adc::RingBufferedAdc;
+use embassy_stm32::adc::{Adc, SampleTime, Sequence};
+use embassy_time::{Instant, Timer};
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    const ADC_BUF_SIZE: usize = 1024;
+    let mut p = embassy_stm32::init(Default::default());
+
+    let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
+    
+    let adc = Adc::new(p.ADC1);
+
+    let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data);
+
+    adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
+    adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112);
+    adc.set_sample_sequence(Sequence::Three, &mut p.PA1, SampleTime::CYCLES112);
+    adc.set_sample_sequence(Sequence::Four, &mut p.PA3, SampleTime::CYCLES112);
+
+    // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around
+    // 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
+    // what channel is at what index is lost. The buffer must be cleared and reset. This *is* handled here, but allowing this to happen will cause
+    // a reduction of performance as each time the buffer is reset, the adc & dma buffer must be restarted.
+
+    // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most
+    // frequently.
+    let mut tic = Instant::now();
+    let mut buffer1: [u16; 256] = [0u16; 256];
+    let _ = adc.start();
+    loop {
+        match adc.read(&mut buffer1).await {
+            Ok(_data) => {
+                let toc = Instant::now();
+                info!(
+                    "\n adc1: {} dt = {}, n = {}",
+                    buffer1[0..16],
+                    (toc - tic).as_micros(),
+                    _data
+                );
+                tic = toc;
+            }
+            Err(e) => {
+                warn!("Error: {:?}", e);
+                buffer1 = [0u16; 256];
+                let _ = adc.start();
+                continue;
+            }
+        }
+
+        Timer::after_micros(300).await;
+    }
+}

From 27b83fdbcfe7e9e64a8b65fdcb8dc82d6dc3e58c Mon Sep 17 00:00:00 2001
From: seth <seth@transastra.com>
Date: Mon, 24 Jun 2024 17:15:16 -0700
Subject: [PATCH 2/5] fmt

---
 examples/stm32f4/src/bin/adc_dma.rs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
index a2611bb6f..88822a507 100644
--- a/examples/stm32f4/src/bin/adc_dma.rs
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -3,8 +3,7 @@
 use cortex_m::singleton;
 use defmt::*;
 use embassy_executor::Spawner;
-use embassy_stm32::adc::RingBufferedAdc;
-use embassy_stm32::adc::{Adc, SampleTime, Sequence};
+use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence};
 use embassy_time::{Instant, Timer};
 use {defmt_rtt as _, panic_probe as _};
 
@@ -14,7 +13,7 @@ async fn main(_spawner: Spawner) {
     let mut p = embassy_stm32::init(Default::default());
 
     let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
-    
+
     let adc = Adc::new(p.ADC1);
 
     let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data);

From 7056783fa23eb25629e1e57da0021916a073a432 Mon Sep 17 00:00:00 2001
From: seth <seth@transastra.com>
Date: Mon, 24 Jun 2024 17:53:59 -0700
Subject: [PATCH 3/5] second adc added to example + API todos completed

---
 embassy-stm32/src/adc/ringbuffered_v2.rs | 14 +++--------
 examples/stm32f4/src/bin/adc_dma.rs      | 32 +++++++++++++++++++-----
 2 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs
index e11311b9a..8c5eb9672 100644
--- a/embassy-stm32/src/adc/ringbuffered_v2.rs
+++ b/embassy-stm32/src/adc/ringbuffered_v2.rs
@@ -315,7 +315,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
     ///
     /// Receive in the background is terminated if an error is returned.
     /// It must then manually be started again by calling `start()` or by re-calling `read()`.
-    pub async fn read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
+    pub fn read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
         let r = T::regs();
 
         // Start background receive if it was not already started
@@ -325,11 +325,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
 
         // Clear overrun flag if set.
         if r.sr().read().ovr() {
-            r.sr().modify(|regs| {
-                regs.set_ovr(false);
-                // regs.set_eoc(false);
-            });
-            // return self.stop(OverrunError);
+            return self.stop(OverrunError);
         }
 
         loop {
@@ -355,11 +351,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
 
         // Clear overrun flag if set.
         if r.sr().read().ovr() {
-            r.sr().modify(|regs| {
-                regs.set_ovr(false);
-                // regs.set_eoc(false);
-            });
-            // return self.stop(OverrunError);
+            return self.stop(OverrunError);
         }
         match self.ring_buf.read_exact(buf).await {
             Ok(len) => Ok(len),
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
index 88822a507..dd19caf1d 100644
--- a/examples/stm32f4/src/bin/adc_dma.rs
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -13,15 +13,18 @@ async fn main(_spawner: Spawner) {
     let mut p = embassy_stm32::init(Default::default());
 
     let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
+    let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
 
     let adc = Adc::new(p.ADC1);
+    let adc2 = Adc::new(p.ADC2);
 
     let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data);
+    let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(p.DMA2_CH2, adc_data2);
 
     adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
     adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112);
-    adc.set_sample_sequence(Sequence::Three, &mut p.PA1, SampleTime::CYCLES112);
-    adc.set_sample_sequence(Sequence::Four, &mut p.PA3, SampleTime::CYCLES112);
+    adc2.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112);
+    adc2.set_sample_sequence(Sequence::Two, &mut p.PA3, SampleTime::CYCLES112);
 
     // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around
     // 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
@@ -31,10 +34,12 @@ async fn main(_spawner: Spawner) {
     // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most
     // frequently.
     let mut tic = Instant::now();
-    let mut buffer1: [u16; 256] = [0u16; 256];
+    let mut buffer1 = [0u16; 256];
+    let mut buffer2 = [0u16; 256];
     let _ = adc.start();
+    let _ = adc2.start();
     loop {
-        match adc.read(&mut buffer1).await {
+        match adc.read_exact(&mut buffer1).await {
             Ok(_data) => {
                 let toc = Instant::now();
                 info!(
@@ -49,10 +54,25 @@ async fn main(_spawner: Spawner) {
                 warn!("Error: {:?}", e);
                 buffer1 = [0u16; 256];
                 let _ = adc.start();
-                continue;
             }
         }
 
-        Timer::after_micros(300).await;
+        match adc2.read_exact(&mut buffer2).await {
+            Ok(_data) => {
+                let toc = Instant::now();
+                info!(
+                    "\n adc2: {} dt = {}, n = {}",
+                    buffer2[0..16],
+                    (toc - tic).as_micros(),
+                    _data
+                );
+                tic = toc;
+            }
+            Err(e) => {
+                warn!("Error: {:?}", e);
+                buffer2 = [0u16; 256];
+                let _ = adc2.start();
+            }
+        }
     }
 }

From 6926e9e07154c72d68ef099d5f28178274f86032 Mon Sep 17 00:00:00 2001
From: seth <seth@transastra.com>
Date: Mon, 24 Jun 2024 23:15:00 -0700
Subject: [PATCH 4/5] CI

---
 examples/stm32f4/src/bin/adc_dma.rs | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
index dd19caf1d..992bed573 100644
--- a/examples/stm32f4/src/bin/adc_dma.rs
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -4,14 +4,19 @@ use cortex_m::singleton;
 use defmt::*;
 use embassy_executor::Spawner;
 use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence};
-use embassy_time::{Instant, Timer};
+use embassy_stm32::Peripherals;
+use embassy_time::Instant;
 use {defmt_rtt as _, panic_probe as _};
 
 #[embassy_executor::main]
-async fn main(_spawner: Spawner) {
-    const ADC_BUF_SIZE: usize = 1024;
-    let mut p = embassy_stm32::init(Default::default());
+async fn main(spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    spawner.must_spawn(adc_task(p));
+}
 
+#[embassy_executor::task]
+async fn adc_task(mut p: Peripherals) {
+    const ADC_BUF_SIZE: usize = 1024;
     let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
     let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
 
@@ -34,8 +39,8 @@ async fn main(_spawner: Spawner) {
     // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most
     // frequently.
     let mut tic = Instant::now();
-    let mut buffer1 = [0u16; 256];
-    let mut buffer2 = [0u16; 256];
+    let mut buffer1 = [0u16; 512];
+    let mut buffer2 = [0u16; 512];
     let _ = adc.start();
     let _ = adc2.start();
     loop {
@@ -52,7 +57,7 @@ async fn main(_spawner: Spawner) {
             }
             Err(e) => {
                 warn!("Error: {:?}", e);
-                buffer1 = [0u16; 256];
+                buffer1 = [0u16; 512];
                 let _ = adc.start();
             }
         }
@@ -70,7 +75,7 @@ async fn main(_spawner: Spawner) {
             }
             Err(e) => {
                 warn!("Error: {:?}", e);
-                buffer2 = [0u16; 256];
+                buffer2 = [0u16; 512];
                 let _ = adc2.start();
             }
         }

From 27652798c72d16cbcf2245ae7d4bd4574001ef1f Mon Sep 17 00:00:00 2001
From: seth <seth@transastra.com>
Date: Mon, 1 Jul 2024 06:44:47 -0700
Subject: [PATCH 5/5] fix typo, add spaces after //

---
 embassy-stm32/src/adc/ringbuffered_v2.rs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs
index 8c5eb9672..fb29d9a8c 100644
--- a/embassy-stm32/src/adc/ringbuffered_v2.rs
+++ b/embassy-stm32/src/adc/ringbuffered_v2.rs
@@ -170,7 +170,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
             Self::start_adc();
         }
 
-        //Check the sequence is long enough
+        // Check the sequence is long enough
         T::regs().sqr1().modify(|r| {
             let prev: Sequence = r.l().into();
             if prev < sequence {
@@ -182,10 +182,10 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
             }
         });
 
-        //Set this GPIO as an analog input.
+        // Set this GPIO as an analog input.
         channel.setup();
 
-        //Set the channel in the right sequence field.
+        // Set the channel in the right sequence field.
         match sequence {
             Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())),
             Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())),
@@ -260,7 +260,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
 
         let r = T::regs();
 
-        //Enable ADC
+        // Enable ADC
         let was_on = Self::is_on();
         if !was_on {
             r.cr2().modify(|reg| {
@@ -298,7 +298,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
             w.set_eocs(vals::Eocs::EACHCONVERSION);
         });
 
-        //Being ADC conversions
+        // Begin ADC conversions
         T::regs().cr2().modify(|reg| {
             reg.set_adon(true);
             reg.set_swstart(true);