diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e74913da8..2f8f8f9e3 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -31,15 +31,15 @@ pub struct Adc<'d, T: Instance> { } pub(crate) mod sealed { - #[cfg(adc_f3)] + #[cfg(any(adc_f3, adc_v1))] use embassy_sync::waitqueue::AtomicWaker; - #[cfg(adc_f3)] + #[cfg(any(adc_f3, adc_v1))] pub struct State { pub waker: AtomicWaker, } - #[cfg(adc_f3)] + #[cfg(any(adc_f3, adc_v1))] impl State { pub const fn new() -> Self { Self { @@ -58,7 +58,7 @@ pub(crate) mod sealed { fn common_regs() -> crate::pac::adccommon::AdcCommon; #[cfg(adc_f3)] fn frequency() -> crate::time::Hertz; - #[cfg(adc_f3)] + #[cfg(any(adc_f3, adc_v1))] fn state() -> &'static State; } @@ -96,7 +96,7 @@ foreach_adc!( unsafe { crate::rcc::get_freqs() }.$clock.unwrap() } - #[cfg(adc_f3)] + #[cfg(any(adc_f3, adc_v1))] fn state() -> &'static sealed::State { static STATE: sealed::State = sealed::State::new(); &STATE diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index e8245884e..15b2dc593 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,13 +1,35 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use crate::interrupt::typelevel::Interrupt; use crate::peripherals::ADC; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + if T::regs().isr().read().eoc() { + T::regs().ier().modify(|w| w.set_eocie(false)); + } else { + return; + } + + T::state().waker.wake(); + } +} + pub struct Vbat; impl InternalChannel for Vbat {} impl super::sealed::InternalChannel for Vbat { @@ -33,7 +55,11 @@ impl super::sealed::InternalChannel for Temperature { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + pub fn new( + adc: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + delay: &mut impl DelayUs, + ) -> Self { into_ref!(adc); T::enable(); T::reset(); @@ -44,12 +70,32 @@ impl<'d, T: Instance> Adc<'d, T> { // tstab = 14 * 1/fadc delay.delay_us(1); - let s = Self { + // A.7.1 ADC calibration code example + T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); + T::regs().cr().modify(|reg| reg.set_adcal(true)); + while T::regs().cr().read().adcal() {} + + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::Interrupt::unpend(); + unsafe { + T::Interrupt::enable(); + } + + Self { adc, sample_time: Default::default(), - }; - s.calibrate(); - s + } } pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat { @@ -80,21 +126,6 @@ impl<'d, T: Instance> Adc<'d, T> { Temperature } - fn calibrate(&self) { - // A.7.1 ADC calibration code example - if T::regs().cr().read().aden() { - T::regs().cr().modify(|reg| reg.set_addis(true)); - } - while T::regs().cr().read().aden() { - // spin - } - T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); - T::regs().cr().modify(|reg| reg.set_adcal(true)); - while T::regs().cr().read().adcal() { - // spin - } - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } @@ -103,57 +134,61 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } - pub fn read

(&mut self, pin: &mut P) -> u16 + pub async fn read

(&mut self, pin: &mut P) -> u16 where P: AdcPin + crate::gpio::sealed::Pin, { let channel = pin.channel(); pin.set_as_analog(); - self.read_channel(channel) + self.read_channel(channel).await } - pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + pub async fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { let channel = channel.channel(); - self.read_channel(channel) + self.read_channel(channel).await } - fn read_channel(&mut self, channel: u8) -> u16 { - // A.7.2 ADC enable sequence code example - if T::regs().isr().read().adrdy() { - T::regs().isr().modify(|reg| reg.set_adrdy(true)); - } - T::regs().cr().modify(|reg| reg.set_aden(true)); - while !T::regs().isr().read().adrdy() { - // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration - // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the - // ADEN bit until the ADRDY flag goes high. - T::regs().cr().modify(|reg| reg.set_aden(true)); - } - + async fn convert(&mut self) -> u16 { T::regs().isr().modify(|reg| { reg.set_eoc(true); reg.set_eosmp(true); }); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); + T::regs().ier().modify(|w| w.set_eocie(true)); + T::regs().cr().modify(|reg| reg.set_adstart(true)); + + poll_fn(|cx| { + T::state().waker.register(cx.waker()); + + if T::regs().isr().read().eoc() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + T::regs().dr().read().data() + } + + async fn read_channel(&mut self, channel: u8) -> u16 { // A.7.5 Single conversion sequence code example - Software trigger T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true)); - T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); - T::regs().cr().modify(|reg| reg.set_adstart(true)); - while !T::regs().isr().read().eoc() { - // spin - } - let value = T::regs().dr().read().0 as u16; - // A.7.3 ADC disable code example - T::regs().cr().modify(|reg| reg.set_adstp(true)); - while T::regs().cr().read().adstp() { - // spin - } - T::regs().cr().modify(|reg| reg.set_addis(true)); - while T::regs().cr().read().aden() { - // spin - } - - value + self.convert().await + } +} + +impl<'d, T: Instance> Drop for Adc<'d, T> { + fn drop(&mut self) { + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() {} + + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() {} + + T::disable(); } } diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index 8ed9f98f8..2ed46a944 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -5,20 +5,26 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::peripherals::ADC; +use embassy_stm32::{adc, bind_interrupts}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ADC1_COMP => adc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC, &mut Delay); + let mut adc = Adc::new(p.ADC, Irqs, &mut Delay); adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; let mut vrefint = adc.enable_vref(&mut Delay); - let vrefint_sample = adc.read_internal(&mut vrefint); + let vrefint_sample = adc.read_internal(&mut vrefint).await; let convert_to_millivolts = |sample| { // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf // 6.3.4 Embedded reference voltage @@ -28,7 +34,7 @@ async fn main(_spawner: Spawner) { }; loop { - let v = adc.read(&mut pin); + let v = adc.read(&mut pin).await; info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; }