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() {} + } + } }