diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-10-10 06:28:41 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-10 06:28:41 +0000 |
commit | ef533e6df48d35b57fcb497e7bb5d6bafd6ca725 (patch) | |
tree | 44d6dfa425e60deb6dd28eb738563b5857d75719 | |
parent | f8fd6ab208fd09142ab9789078e9b23ba8c3e6a9 (diff) | |
parent | 322cfafed353ddfbe62121238ef97c56dd7a6eed (diff) | |
download | embassy-ef533e6df48d35b57fcb497e7bb5d6bafd6ca725.zip |
Merge #1004
1004: Fix internal channels for adc v2 r=lulf a=chemicstry
Internal channel reading was broken on adc_v2, because `Adc::read()` requires gpio pin trait, which was not implemented by `VrefInt`, `Temperature`, `Vbat`. The required configuration bits `tsvrefe`, `vbate` were not enabled either. This PR makes it a bit closer to how adc_v4 works.
While at it, I also changed adc_v2 to use `RccPeripheral` instead of permanently enabling all ADCs.
Co-authored-by: chemicstry <chemicstry@gmail.com>
-rw-r--r-- | embassy-stm32/src/adc/mod.rs | 9 | ||||
-rw-r--r-- | embassy-stm32/src/adc/v2.rs | 161 | ||||
-rw-r--r-- | embassy-stm32/src/adc/v4.rs | 16 | ||||
-rw-r--r-- | examples/stm32f4/src/bin/adc.rs | 25 |
4 files changed, 138 insertions, 73 deletions
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 8da13073..0eb4eba7 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -28,15 +28,20 @@ pub(crate) mod sealed { pub trait AdcPin<T: Instance> { fn channel(&self) -> u8; } + + pub trait InternalChannel<T> { + fn channel(&self) -> u8; + } } -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_v2)))] pub trait Instance: sealed::Instance + 'static {} -#[cfg(adc_f1)] +#[cfg(any(adc_f1, adc_v2))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} #[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common: sealed::Common + 'static {} pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} +pub trait InternalChannel<T>: sealed::InternalChannel<T> {} #[cfg(not(stm32h7))] foreach_peripheral!( diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 25b7ba96..4fe4ad1f 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -3,7 +3,9 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; +use super::InternalChannel; use crate::adc::{AdcPin, Instance}; +use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -12,20 +14,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -#[cfg(not(any(rcc_f4, rcc_f7)))] -fn enable() { - todo!() -} - -#[cfg(any(rcc_f4, rcc_f7))] -fn enable() { - critical_section::with(|_| unsafe { - // TODO do not enable all adc clocks if not needed - crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true)); - crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true)); - crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true)); - }); -} +/// ADC turn-on time +pub const ADC_POWERUP_TIME_US: u32 = 3; pub enum Resolution { TwelveBit, @@ -61,24 +51,53 @@ impl Resolution { } pub struct VrefInt; -impl<T: Instance> AdcPin<T> for VrefInt {} -impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { +impl InternalChannel<ADC1> for VrefInt {} +impl super::sealed::InternalChannel<ADC1> for VrefInt { fn channel(&self) -> u8 { 17 } } +impl VrefInt { + /// Time needed for internal voltage reference to stabilize + pub fn start_time_us() -> u32 { + 10 + } +} + pub struct Temperature; -impl<T: Instance> AdcPin<T> for Temperature {} -impl<T: Instance> super::sealed::AdcPin<T> for Temperature { +impl InternalChannel<ADC1> for Temperature {} +impl super::sealed::InternalChannel<ADC1> for Temperature { fn channel(&self) -> u8 { - 16 + cfg_if::cfg_if! { + if #[cfg(any(stm32f40, stm32f41))] { + 16 + } else { + 18 + } + } + } +} + +impl Temperature { + /// Converts temperature sensor reading in millivolts to degrees celcius + pub fn to_celcius(sample_mv: u16) -> f32 { + // From 6.3.22 Temperature sensor characteristics + const V25: i32 = 760; // mV + const AVG_SLOPE: f32 = 2.5; // mV/C + + (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 + } + + /// Time needed for temperature sensor readings to stabilize + pub fn start_time_us() -> u32 { + 10 } } pub struct Vbat; -impl<T: Instance> AdcPin<T> for Vbat {} -impl<T: Instance> super::sealed::AdcPin<T> for Vbat { +impl InternalChannel<ADC1> for Vbat {} +impl super::sealed::InternalChannel<ADC1> for Vbat { fn channel(&self) -> u8 { 18 } @@ -164,21 +183,19 @@ where { pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { into_ref!(_peri); - enable(); + T::enable(); + T::reset(); - let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) }; + let presc = Prescaler::from_pclk2(T::frequency()); unsafe { T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); - } - unsafe { - // disable before config is set T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); + reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); }); } - delay.delay_us(20); // TODO? + delay.delay_us(ADC_POWERUP_TIME_US); Self { sample_time: Default::default(), @@ -208,6 +225,45 @@ where ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } + /// Enables internal voltage reference and returns [VrefInt], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vrefint(&self) -> VrefInt { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); + } + + VrefInt {} + } + + /// Enables internal temperature sensor and returns [Temperature], which can be used in + /// [Adc::read_internal()] to perform conversion. + /// + /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, + /// temperature sensor will return vbat value. + pub fn enable_temperature(&self) -> Temperature { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); + } + + Temperature {} + } + + /// Enables vbat input and returns [Vbat], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vbat(&self) -> Vbat { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED); + }); + } + + Vbat {} + } + /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { @@ -238,42 +294,29 @@ where P: crate::gpio::sealed::Pin, { unsafe { - // dissable ADC - T::regs().cr2().modify(|reg| { - reg.set_swstart(false); - }); - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); - }); - pin.set_as_analog(); - // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); + self.read_channel(pin.channel()) + } + } - // Select channel - T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); + pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 { + unsafe { self.read_channel(channel.channel()) } + } - // Configure channel - Self::set_channel_sample_time(pin.channel(), self.sample_time); + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // Configure ADC + T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); - // enable adc - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); - }); + // Select channel + T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); - let val = self.convert(); + // Configure channel + Self::set_channel_sample_time(channel, self.sample_time); - // dissable ADC - T::regs().cr2().modify(|reg| { - reg.set_swstart(false); - }); - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); - }); + let val = self.convert(); - val - } + val } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { @@ -288,3 +331,9 @@ where } } } + +impl<'d, T: Instance> Drop for Adc<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index d356d7b6..eda2b2a7 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance}; +use super::{AdcPin, Instance, InternalChannel}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -50,18 +50,10 @@ impl Resolution { } } -pub trait InternalChannel<T>: sealed::InternalChannel<T> {} - -mod sealed { - pub trait InternalChannel<T> { - fn channel(&self) -> u8; - } -} - // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl<T: Instance> InternalChannel<T> for VrefInt {} -impl<T: Instance> sealed::InternalChannel<T> for VrefInt { +impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { fn channel(&self) -> u8 { 19 } @@ -69,7 +61,7 @@ impl<T: Instance> sealed::InternalChannel<T> for VrefInt { pub struct Temperature; impl<T: Instance> InternalChannel<T> for Temperature {} -impl<T: Instance> sealed::InternalChannel<T> for Temperature { +impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { fn channel(&self) -> u8 { 18 } @@ -77,7 +69,7 @@ impl<T: Instance> sealed::InternalChannel<T> for Temperature { pub struct Vbat; impl<T: Instance> InternalChannel<T> for Vbat {} -impl<T: Instance> sealed::InternalChannel<T> for Vbat { +impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { fn channel(&self) -> u8 { // TODO this should be 14 for H7a/b/35 17 diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 87118507..1d030f7d 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -2,9 +2,10 @@ #![no_main] #![feature(type_alias_impl_trait)] +use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::Adc; +use embassy_stm32::adc::{Adc, Temperature, VrefInt}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -13,12 +14,30 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut delay = Delay; + let mut adc = Adc::new(p.ADC1, &mut delay); let mut pin = p.PC1; + let mut vrefint = adc.enable_vrefint(); + let mut temp = adc.enable_temperature(); + + // Startup delay can be combined to the maximum of either + delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); + loop { + // Read pin let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); + + // Read internal temperature + let v = adc.read_internal(&mut temp); + let celcius = Temperature::to_celcius(adc.to_millivolts(v)); + info!("Internal temp: {} ({} C)", v, celcius); + + // Read internal voltage reference + let v = adc.read_internal(&mut vrefint); + info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; } } |