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/v3.rs b/embassy-stm32/src/adc/v3.rs index bf7ad242b..c876bffea 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,9 +1,12 @@ use cfg_if::cfg_if; use embassy_hal_internal::into_ref; +use pac::adc::vals::Dmacfg; -use super::blocking_delay_us; -use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; -use crate::{rcc, Peripheral}; +use super::{ + blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, +}; +use crate::dma::Transfer; +use crate::{pac, rcc, Peripheral}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -12,7 +15,7 @@ pub const VREF_CALIB_MV: u32 = 3000; pub struct VrefInt; impl AdcChannel for VrefInt {} -impl super::SealedAdcChannel for VrefInt { +impl SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -31,7 +34,7 @@ impl super::SealedAdcChannel for VrefInt { pub struct Temperature; impl AdcChannel for Temperature {} -impl super::SealedAdcChannel for Temperature { +impl SealedAdcChannel for Temperature { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -50,7 +53,7 @@ impl super::SealedAdcChannel for Temperature { pub struct Vbat; impl AdcChannel for Vbat {} -impl super::SealedAdcChannel for Vbat { +impl SealedAdcChannel for Vbat { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -101,6 +104,7 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_advregen(true); }); + // If this is false then each ADC_CHSELR bit enables an input channel. #[cfg(any(adc_g0, adc_u0))] T::regs().cfgr1().modify(|reg| { reg.set_chselrmod(false); @@ -124,6 +128,28 @@ impl<'d, T: Instance> Adc<'d, T> { } } + // Enable ADC only when it is not already running. + fn enable(&mut self) { + // Make sure bits are off + while T::regs().cr().read().addis() { + // spin + } + + if !T::regs().cr().read().aden() { + // Enable ADC + T::regs().isr().modify(|reg| { + reg.set_adrdy(true); + }); + T::regs().cr().modify(|reg| { + reg.set_aden(true); + }); + + while !T::regs().isr().read().adrdy() { + // spin + } + } + } + pub fn enable_vrefint(&self) -> VrefInt { #[cfg(not(any(adc_g0, adc_u0)))] T::common_regs().ccr().modify(|reg| { @@ -181,10 +207,17 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat {} } + /// Set the ADC sample time. pub fn set_sample_time(&mut self, sample_time: SampleTime) { 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) { #[cfg(not(any(adc_g0, adc_u0)))] T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); @@ -220,24 +253,140 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } + /// Read an ADC channel. pub fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { - // Make sure bits are off - while T::regs().cr().read().addis() { - // spin + self.read_channel(channel) + } + + /// Asynchronously read from sequence of ADC channels. + pub async fn read_async( + &mut self, + rx_dma: &mut impl RxDma, + sequence: impl ExactSizeIterator, SampleTime)>, + readings: &mut [u16], + ) { + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() == readings.len(), + "Sequence length must be equal to readings length" + ); + assert!( + sequence.len() <= 16, + "Asynchronous read sequence cannot be more than 16 in length" + ); + + // Ensure no conversions are ongoing and ADC is enabled. + Self::cancel_conversions(); + self.enable(); + + // Set sequence length + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().sqr1().modify(|w| { + w.set_l(sequence.len() as u8 - 1); + }); + + #[cfg(any(adc_g0, adc_u0))] + let mut channel_mask = 0; + + // Configure channels and ranks + for (_i, (channel, sample_time)) in sequence.enumerate() { + Self::configure_channel(channel, sample_time); + + // Each channel is sampled according to sequence + #[cfg(not(any(adc_g0, adc_u0)))] + 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!(), + } + + #[cfg(any(adc_g0, adc_u0))] + { + channel_mask |= 1 << channel.channel(); + } } - // Enable ADC + // On G0 and U0 enabled channels are sampled from 0 to last channel. + // It is possible to add up to 8 sequences if CHSELRMOD = 1. + // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. + #[cfg(any(adc_g0, adc_u0))] + T::regs().chselr().modify(|reg| { + reg.set_chsel(channel_mask); + }); + + // Set continuous mode with oneshot dma. + // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { - reg.set_adrdy(true); + reg.set_ovr(true); }); + + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::ONESHOT); + reg.set_dmaen(true); + }); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::ONESHOT); + reg.set_dmaen(true); + }); + + let request = rx_dma.request(); + let transfer = unsafe { + Transfer::new_read( + rx_dma, + request, + T::regs().dr().as_ptr() as *mut u16, + readings, + Default::default(), + ) + }; + + // Start conversion T::regs().cr().modify(|reg| { - reg.set_aden(true); + reg.set_adstart(true); }); - while !T::regs().isr().read().adrdy() { - // spin - } + // Wait for conversion sequence to finish. + transfer.await; + // Ensure conversions are finished. + Self::cancel_conversions(); + + // Reset configuration. + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + }); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + }); + } + + fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." #[cfg(adc_h5)] @@ -246,7 +395,12 @@ impl<'d, T: Instance> Adc<'d, T> { } // Configure channel - Self::set_channel_sample_time(channel.channel(), self.sample_time); + Self::set_channel_sample_time(channel.channel(), sample_time); + } + + fn read_channel(&mut self, channel: &mut impl AdcChannel) -> u16 { + self.enable(); + Self::configure_channel(channel, self.sample_time); // Select channel #[cfg(not(any(adc_g0, adc_u0)))] @@ -262,7 +416,6 @@ impl<'d, T: Instance> Adc<'d, T> { // STM32G4: Section 2.7.3 #[cfg(any(rcc_l4, rcc_g4))] let _ = self.convert(); - let val = self.convert(); T::regs().cr().modify(|reg| reg.set_addis(true)); @@ -295,6 +448,7 @@ impl<'d, T: Instance> Adc<'d, T> { fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { cfg_if! { if #[cfg(any(adc_g0, adc_u0))] { + // On G0 and U6 all channels use the same sampling time. T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } else if #[cfg(adc_h5)] { match _ch { @@ -309,4 +463,13 @@ impl<'d, T: Instance> Adc<'d, T> { } } } + + fn cancel_conversions() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(true); + }); + while T::regs().cr().read().adstart() {} + } + } } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b15aebc10..7db6fa4a2 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,8 +1,11 @@ #[allow(unused)] -use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; +use pac::adc::vals::{Adcaldif, Adstp, Boost, Difsel, Dmngt, Exten, Pcsel}; 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 +37,7 @@ const VBAT_CHANNEL: u8 = 17; /// Internal voltage reference channel. pub struct VrefInt; impl AdcChannel for VrefInt {} -impl super::SealedAdcChannel for VrefInt { +impl SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { VREF_CHANNEL } @@ -43,7 +46,7 @@ impl super::SealedAdcChannel for VrefInt { /// Internal temperature channel. pub struct Temperature; impl AdcChannel for Temperature {} -impl super::SealedAdcChannel for Temperature { +impl SealedAdcChannel for Temperature { fn channel(&self) -> u8 { TEMP_CHANNEL } @@ -52,7 +55,7 @@ impl super::SealedAdcChannel for Temperature { /// Internal battery voltage channel. pub struct Vbat; impl AdcChannel for Vbat {} -impl super::SealedAdcChannel for Vbat { +impl SealedAdcChannel for Vbat { fn channel(&self) -> u8 { VBAT_CHANNEL } @@ -262,6 +265,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())); @@ -311,25 +319,123 @@ impl<'d, T: Instance> Adc<'d, T> { /// Read an ADC channel. pub fn read(&mut self, channel: &mut impl AdcChannel) -> 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, + sequence: impl ExactSizeIterator, SampleTime)>, + readings: &mut [u16], + ) { + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() == readings.len(), + "Sequence length must be equal to readings length" + ); + 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, + readings, + 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, 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) -> u16 { + Self::configure_channel(channel, self.sample_time); + + T::regs().sqr1().modify(|reg| { + reg.set_sq(0, channel.channel()); reg.set_l(0); }); @@ -344,4 +450,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() {} + } + } } diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs new file mode 100644 index 000000000..42d1e729b --- /dev/null +++ b/examples/stm32g0/src/bin/adc_dma.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +static mut DMA_BUF: [u16; 2] = [0; 2]; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut read_buffer = unsafe { &mut DMA_BUF[..] }; + + let p = embassy_stm32::init(Default::default()); + + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC1); + + let mut dma = p.DMA1_CH1; + let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); + let mut pa0 = p.PA0.degrade_adc(); + + loop { + adc.read_async( + &mut dma, + [ + (&mut vrefint_channel, SampleTime::CYCLES160_5), + (&mut pa0, SampleTime::CYCLES160_5), + ] + .into_iter(), + &mut read_buffer, + ) + .await; + + let vrefint = read_buffer[0]; + let measured = read_buffer[1]; + info!("vrefint: {}", vrefint); + info!("measured: {}", measured); + Timer::after_millis(500).await; + } +} diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs new file mode 100644 index 000000000..6c0240453 --- /dev/null +++ b/examples/stm32h7/src/bin/adc_dma.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[link_section = ".ram_d3"] +static mut DMA_BUF: [u16; 2] = [0; 2]; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut read_buffer = unsafe { &mut DMA_BUF[..] }; + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + config.rcc.pll1 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.mux.adcsel = mux::Adcsel::PLL2_P; + } + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC3); + + let mut dma = p.DMA1_CH1; + let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); + let mut pc0 = p.PC0.degrade_adc(); + + loop { + adc.read_async( + &mut dma, + [ + (&mut vrefint_channel, SampleTime::CYCLES387_5), + (&mut pc0, SampleTime::CYCLES810_5), + ] + .into_iter(), + &mut read_buffer, + ) + .await; + + let vrefint = read_buffer[0]; + let measured = read_buffer[1]; + info!("vrefint: {}", vrefint); + info!("measured: {}", measured); + Timer::after_millis(500).await; + } +}