From 511a95124690be253dc9b789abeae1bdea3aa83a Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:18:37 -0400 Subject: [PATCH] Differentiate between `read` and `read_internal` for STM32F0 ADC The internal channels (vbat, vref, and temperature) are not real pins and do not have the `set_as_analog` method. They must be read using the `read_internal` method. --- embassy-stm32/src/adc/v1.rs | 120 +++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 224d1717..f787f72a 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -48,25 +48,33 @@ impl Resolution { } } +pub trait InternalChannel: sealed::InternalChannel {} + +mod sealed { + pub trait InternalChannel { + fn channel(&self) -> u8; + } +} + pub struct Vbat; -impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl InternalChannel for Vbat {} +impl sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl InternalChannel for Vref {} +impl sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl InternalChannel for Temperature {} +impl sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -219,64 +227,64 @@ impl<'d, T: Instance> Adc<'d, T> { ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } - fn convert(&mut self) -> u16 { - unsafe { - T::regs().isr().modify(|reg| { - reg.set_eoc(true); - reg.set_eosmp(true); - }); - - // A.7.5 Single conversion sequence code example - Software trigger - T::regs().cr().modify(|reg| reg.set_adstart(true)); - while !T::regs().isr().read().eoc() { - // spin - } - - T::regs().dr().read().0 as u16 - } - } - pub fn read

(&mut self, pin: &mut P) -> u16 where P: AdcPin + crate::gpio::sealed::Pin, { + let channel = pin.channel(); unsafe { - // 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::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); - Self::set_channel_sample_time(pin.channel(), self.sample_time); pin.set_as_analog(); - T::regs() - .chselr() - .write(|reg| reg.set_chselx(pin.channel() as usize, true)); - - let value = self.convert(); - - // 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.read_channel(channel) } } - unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { - T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + let channel = channel.channel(); + unsafe { + self.read_channel(channel) + } + } + + unsafe 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)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // 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.sample_time())); + 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 } }