From 02b096915fbf138602e2ad8c6e1d85d531882daf Mon Sep 17 00:00:00 2001
From: Alexandros Liarokapis <liarokapis.v@gmail.com>
Date: Fri, 21 Jun 2024 23:37:58 +0300
Subject: [PATCH] add asynchrous sequence read support to adc v4

---
 embassy-stm32/build.rs      |   1 +
 embassy-stm32/src/adc/v4.rs | 139 ++++++++++++++++++++++++++++++++----
 2 files changed, 127 insertions(+), 13 deletions(-)

diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 24e2226a2..df866b5ff 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1182,6 +1182,7 @@ fn main() {
         (("adc", "ADC1"), quote!(crate::adc::RxDma)),
         (("adc", "ADC2"), quote!(crate::adc::RxDma)),
         (("adc", "ADC3"), quote!(crate::adc::RxDma)),
+        (("adc", "ADC4"), 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/v4.rs b/embassy-stm32/src/adc/v4.rs
index 50db646fe..f4b62d80e 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -1,8 +1,12 @@
 #[allow(unused)]
 use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
+use pac::adc::vals::{Adstp, Dmngt};
 use pac::adccommon::vals::Presc;
 
-use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime};
+use super::{
+    blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel,
+};
+use crate::dma::Transfer;
 use crate::time::Hertz;
 use crate::{pac, rcc, Peripheral};
 
@@ -34,7 +38,7 @@ const VBAT_CHANNEL: u8 = 17;
 /// Internal voltage reference channel.
 pub struct VrefInt;
 impl<T: Instance> AdcChannel<T> for VrefInt {}
-impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
+impl<T: Instance> SealedAdcChannel<T> for VrefInt {
     fn channel(&self) -> u8 {
         VREF_CHANNEL
     }
@@ -43,7 +47,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
 /// Internal temperature channel.
 pub struct Temperature;
 impl<T: Instance> AdcChannel<T> for Temperature {}
-impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
+impl<T: Instance> SealedAdcChannel<T> for Temperature {
     fn channel(&self) -> u8 {
         TEMP_CHANNEL
     }
@@ -52,7 +56,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
 /// Internal battery voltage channel.
 pub struct Vbat;
 impl<T: Instance> AdcChannel<T> for Vbat {}
-impl<T: Instance> super::SealedAdcChannel<T> for Vbat {
+impl<T: Instance> SealedAdcChannel<T> for Vbat {
     fn channel(&self) -> u8 {
         VBAT_CHANNEL
     }
@@ -247,6 +251,11 @@ impl<'d, T: Instance> Adc<'d, T> {
         self.sample_time = sample_time;
     }
 
+    /// Get the ADC sample time.
+    pub fn sample_time(&self) -> SampleTime {
+        self.sample_time
+    }
+
     /// Set the ADC resolution.
     pub fn set_resolution(&mut self, resolution: Resolution) {
         T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
@@ -273,25 +282,120 @@ impl<'d, T: Instance> Adc<'d, T> {
 
     /// Read an ADC channel.
     pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
-        channel.setup();
-
-        self.read_channel(channel.channel())
+        self.read_channel(channel)
     }
 
-    fn read_channel(&mut self, channel: u8) -> u16 {
-        // Configure channel
-        Self::set_channel_sample_time(channel, self.sample_time);
+    /// Asynchronously read from sequence of ADC channels.
+    pub async fn read_async(
+        &mut self,
+        rx_dma: &mut impl RxDma<T>,
+        sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
+        data: &mut [u16],
+    ) {
+        assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
+
+        assert!(
+            sequence.len() <= 16,
+            "Asynchronous read sequence cannot be more than 16 in length"
+        );
+
+        // Ensure no conversions are ongoing
+        Self::cancel_conversions();
+
+        // Set sequence length
+        T::regs().sqr1().modify(|w| {
+            w.set_l(sequence.len() as u8 - 1);
+        });
+
+        // Configure channels and ranks
+        for (i, (channel, sample_time)) in sequence.enumerate() {
+            Self::configure_channel(channel, sample_time);
+            match i {
+                0..=3 => {
+                    T::regs().sqr1().modify(|w| {
+                        w.set_sq(i, channel.channel());
+                    });
+                }
+                4..=8 => {
+                    T::regs().sqr2().modify(|w| {
+                        w.set_sq(i - 4, channel.channel());
+                    });
+                }
+                9..=13 => {
+                    T::regs().sqr3().modify(|w| {
+                        w.set_sq(i - 9, channel.channel());
+                    });
+                }
+                14..=15 => {
+                    T::regs().sqr4().modify(|w| {
+                        w.set_sq(i - 14, channel.channel());
+                    });
+                }
+                _ => unreachable!(),
+            }
+        }
+
+        // Set continuous mode with oneshot dma.
+        // Clear overrun flag before starting transfer.
+
+        T::regs().isr().modify(|reg| {
+            reg.set_ovr(true);
+        });
+        T::regs().cfgr().modify(|reg| {
+            reg.set_cont(true);
+            reg.set_dmngt(Dmngt::DMA_ONESHOT);
+        });
+
+        let request = rx_dma.request();
+        let transfer = unsafe {
+            Transfer::new_read(
+                rx_dma,
+                request,
+                T::regs().dr().as_ptr() as *mut u16,
+                data,
+                Default::default(),
+            )
+        };
+
+        // Start conversion
+        T::regs().cr().modify(|reg| {
+            reg.set_adstart(true);
+        });
+
+        // Wait for conversion sequence to finish.
+        transfer.await;
+
+        // Ensure conversions are finished.
+        Self::cancel_conversions();
+
+        // Reset configuration.
+        T::regs().cfgr().modify(|reg| {
+            reg.set_cont(false);
+            reg.set_dmngt(Dmngt::from_bits(0));
+        });
+    }
+
+    fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
+        channel.setup();
+
+        let channel = channel.channel();
+
+        Self::set_channel_sample_time(channel, sample_time);
 
         #[cfg(stm32h7)]
         {
             T::regs().cfgr2().modify(|w| w.set_lshift(0));
             T::regs()
                 .pcsel()
-                .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
+                .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
         }
+    }
 
-        T::regs().sqr1().write(|reg| {
-            reg.set_sq(0, channel);
+    fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
+        Self::configure_channel(channel, self.sample_time);
+
+        T::regs().sqr1().modify(|reg| {
+            reg.set_sq(0, channel.channel());
             reg.set_l(0);
         });
 
@@ -306,4 +410,13 @@ impl<'d, T: Instance> Adc<'d, T> {
             T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
         }
     }
+
+    fn cancel_conversions() {
+        if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
+            T::regs().cr().modify(|reg| {
+                reg.set_adstp(Adstp::STOP);
+            });
+            while T::regs().cr().read().adstart() {}
+        }
+    }
 }