diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-10-10 06:28:41 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-10-10 06:28:41 +0000 |
| commit | ef533e6df48d35b57fcb497e7bb5d6bafd6ca725 (patch) | |
| tree | 44d6dfa425e60deb6dd28eb738563b5857d75719 | |
| parent | f8fd6ab208fd09142ab9789078e9b23ba8c3e6a9 (diff) | |
| parent | 322cfafed353ddfbe62121238ef97c56dd7a6eed (diff) | |
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 <[email protected]>
| -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 8da13073e..0eb4eba73 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -28,15 +28,20 @@ pub(crate) mod sealed { | |||
| 28 | pub trait AdcPin<T: Instance> { | 28 | pub trait AdcPin<T: Instance> { |
| 29 | fn channel(&self) -> u8; | 29 | fn channel(&self) -> u8; |
| 30 | } | 30 | } |
| 31 | |||
| 32 | pub trait InternalChannel<T> { | ||
| 33 | fn channel(&self) -> u8; | ||
| 34 | } | ||
| 31 | } | 35 | } |
| 32 | 36 | ||
| 33 | #[cfg(not(adc_f1))] | 37 | #[cfg(not(any(adc_f1, adc_v2)))] |
| 34 | pub trait Instance: sealed::Instance + 'static {} | 38 | pub trait Instance: sealed::Instance + 'static {} |
| 35 | #[cfg(adc_f1)] | 39 | #[cfg(any(adc_f1, adc_v2))] |
| 36 | pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} | 40 | pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} |
| 37 | #[cfg(all(not(adc_f1), not(adc_v1)))] | 41 | #[cfg(all(not(adc_f1), not(adc_v1)))] |
| 38 | pub trait Common: sealed::Common + 'static {} | 42 | pub trait Common: sealed::Common + 'static {} |
| 39 | pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} | 43 | pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} |
| 44 | pub trait InternalChannel<T>: sealed::InternalChannel<T> {} | ||
| 40 | 45 | ||
| 41 | #[cfg(not(stm32h7))] | 46 | #[cfg(not(stm32h7))] |
| 42 | foreach_peripheral!( | 47 | foreach_peripheral!( |
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 25b7ba967..4fe4ad1f0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -3,7 +3,9 @@ use core::marker::PhantomData; | |||
| 3 | use embassy_hal_common::into_ref; | 3 | use embassy_hal_common::into_ref; |
| 4 | use embedded_hal_02::blocking::delay::DelayUs; | 4 | use embedded_hal_02::blocking::delay::DelayUs; |
| 5 | 5 | ||
| 6 | use super::InternalChannel; | ||
| 6 | use crate::adc::{AdcPin, Instance}; | 7 | use crate::adc::{AdcPin, Instance}; |
| 8 | use crate::peripherals::ADC1; | ||
| 7 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 8 | use crate::Peripheral; | 10 | use crate::Peripheral; |
| 9 | 11 | ||
| @@ -12,20 +14,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 12 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 14 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 13 | pub const VREF_CALIB_MV: u32 = 3300; | 15 | pub const VREF_CALIB_MV: u32 = 3300; |
| 14 | 16 | ||
| 15 | #[cfg(not(any(rcc_f4, rcc_f7)))] | 17 | /// ADC turn-on time |
| 16 | fn enable() { | 18 | pub const ADC_POWERUP_TIME_US: u32 = 3; |
| 17 | todo!() | ||
| 18 | } | ||
| 19 | |||
| 20 | #[cfg(any(rcc_f4, rcc_f7))] | ||
| 21 | fn enable() { | ||
| 22 | critical_section::with(|_| unsafe { | ||
| 23 | // TODO do not enable all adc clocks if not needed | ||
| 24 | crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true)); | ||
| 25 | crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true)); | ||
| 26 | crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true)); | ||
| 27 | }); | ||
| 28 | } | ||
| 29 | 19 | ||
| 30 | pub enum Resolution { | 20 | pub enum Resolution { |
| 31 | TwelveBit, | 21 | TwelveBit, |
| @@ -61,24 +51,53 @@ impl Resolution { | |||
| 61 | } | 51 | } |
| 62 | 52 | ||
| 63 | pub struct VrefInt; | 53 | pub struct VrefInt; |
| 64 | impl<T: Instance> AdcPin<T> for VrefInt {} | 54 | impl InternalChannel<ADC1> for VrefInt {} |
| 65 | impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { | 55 | impl super::sealed::InternalChannel<ADC1> for VrefInt { |
| 66 | fn channel(&self) -> u8 { | 56 | fn channel(&self) -> u8 { |
| 67 | 17 | 57 | 17 |
| 68 | } | 58 | } |
| 69 | } | 59 | } |
| 70 | 60 | ||
| 61 | impl VrefInt { | ||
| 62 | /// Time needed for internal voltage reference to stabilize | ||
| 63 | pub fn start_time_us() -> u32 { | ||
| 64 | 10 | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 71 | pub struct Temperature; | 68 | pub struct Temperature; |
| 72 | impl<T: Instance> AdcPin<T> for Temperature {} | 69 | impl InternalChannel<ADC1> for Temperature {} |
| 73 | impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | 70 | impl super::sealed::InternalChannel<ADC1> for Temperature { |
| 74 | fn channel(&self) -> u8 { | 71 | fn channel(&self) -> u8 { |
| 75 | 16 | 72 | cfg_if::cfg_if! { |
| 73 | if #[cfg(any(stm32f40, stm32f41))] { | ||
| 74 | 16 | ||
| 75 | } else { | ||
| 76 | 18 | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | impl Temperature { | ||
| 83 | /// Converts temperature sensor reading in millivolts to degrees celcius | ||
| 84 | pub fn to_celcius(sample_mv: u16) -> f32 { | ||
| 85 | // From 6.3.22 Temperature sensor characteristics | ||
| 86 | const V25: i32 = 760; // mV | ||
| 87 | const AVG_SLOPE: f32 = 2.5; // mV/C | ||
| 88 | |||
| 89 | (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Time needed for temperature sensor readings to stabilize | ||
| 93 | pub fn start_time_us() -> u32 { | ||
| 94 | 10 | ||
| 76 | } | 95 | } |
| 77 | } | 96 | } |
| 78 | 97 | ||
| 79 | pub struct Vbat; | 98 | pub struct Vbat; |
| 80 | impl<T: Instance> AdcPin<T> for Vbat {} | 99 | impl InternalChannel<ADC1> for Vbat {} |
| 81 | impl<T: Instance> super::sealed::AdcPin<T> for Vbat { | 100 | impl super::sealed::InternalChannel<ADC1> for Vbat { |
| 82 | fn channel(&self) -> u8 { | 101 | fn channel(&self) -> u8 { |
| 83 | 18 | 102 | 18 |
| 84 | } | 103 | } |
| @@ -164,21 +183,19 @@ where | |||
| 164 | { | 183 | { |
| 165 | pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { | 184 | pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { |
| 166 | into_ref!(_peri); | 185 | into_ref!(_peri); |
| 167 | enable(); | 186 | T::enable(); |
| 187 | T::reset(); | ||
| 168 | 188 | ||
| 169 | let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) }; | 189 | let presc = Prescaler::from_pclk2(T::frequency()); |
| 170 | unsafe { | 190 | unsafe { |
| 171 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | 191 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); |
| 172 | } | ||
| 173 | 192 | ||
| 174 | unsafe { | ||
| 175 | // disable before config is set | ||
| 176 | T::regs().cr2().modify(|reg| { | 193 | T::regs().cr2().modify(|reg| { |
| 177 | reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); | 194 | reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); |
| 178 | }); | 195 | }); |
| 179 | } | 196 | } |
| 180 | 197 | ||
| 181 | delay.delay_us(20); // TODO? | 198 | delay.delay_us(ADC_POWERUP_TIME_US); |
| 182 | 199 | ||
| 183 | Self { | 200 | Self { |
| 184 | sample_time: Default::default(), | 201 | sample_time: Default::default(), |
| @@ -208,6 +225,45 @@ where | |||
| 208 | ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 | 225 | ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 |
| 209 | } | 226 | } |
| 210 | 227 | ||
| 228 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | ||
| 229 | /// [Adc::read_internal()] to perform conversion. | ||
| 230 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 231 | unsafe { | ||
| 232 | T::common_regs().ccr().modify(|reg| { | ||
| 233 | reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); | ||
| 234 | }); | ||
| 235 | } | ||
| 236 | |||
| 237 | VrefInt {} | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Enables internal temperature sensor and returns [Temperature], which can be used in | ||
| 241 | /// [Adc::read_internal()] to perform conversion. | ||
| 242 | /// | ||
| 243 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 244 | /// temperature sensor will return vbat value. | ||
| 245 | pub fn enable_temperature(&self) -> Temperature { | ||
| 246 | unsafe { | ||
| 247 | T::common_regs().ccr().modify(|reg| { | ||
| 248 | reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); | ||
| 249 | }); | ||
| 250 | } | ||
| 251 | |||
| 252 | Temperature {} | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Enables vbat input and returns [Vbat], which can be used in | ||
| 256 | /// [Adc::read_internal()] to perform conversion. | ||
| 257 | pub fn enable_vbat(&self) -> Vbat { | ||
| 258 | unsafe { | ||
| 259 | T::common_regs().ccr().modify(|reg| { | ||
| 260 | reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED); | ||
| 261 | }); | ||
| 262 | } | ||
| 263 | |||
| 264 | Vbat {} | ||
| 265 | } | ||
| 266 | |||
| 211 | /// Perform a single conversion. | 267 | /// Perform a single conversion. |
| 212 | fn convert(&mut self) -> u16 { | 268 | fn convert(&mut self) -> u16 { |
| 213 | unsafe { | 269 | unsafe { |
| @@ -238,42 +294,29 @@ where | |||
| 238 | P: crate::gpio::sealed::Pin, | 294 | P: crate::gpio::sealed::Pin, |
| 239 | { | 295 | { |
| 240 | unsafe { | 296 | unsafe { |
| 241 | // dissable ADC | ||
| 242 | T::regs().cr2().modify(|reg| { | ||
| 243 | reg.set_swstart(false); | ||
| 244 | }); | ||
| 245 | T::regs().cr2().modify(|reg| { | ||
| 246 | reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); | ||
| 247 | }); | ||
| 248 | |||
| 249 | pin.set_as_analog(); | 297 | pin.set_as_analog(); |
| 250 | 298 | ||
| 251 | // Configure ADC | 299 | self.read_channel(pin.channel()) |
| 252 | T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); | 300 | } |
| 301 | } | ||
| 253 | 302 | ||
| 254 | // Select channel | 303 | pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 { |
| 255 | T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); | 304 | unsafe { self.read_channel(channel.channel()) } |
| 305 | } | ||
| 256 | 306 | ||
| 257 | // Configure channel | 307 | unsafe fn read_channel(&mut self, channel: u8) -> u16 { |
| 258 | Self::set_channel_sample_time(pin.channel(), self.sample_time); | 308 | // Configure ADC |
| 309 | T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); | ||
| 259 | 310 | ||
| 260 | // enable adc | 311 | // Select channel |
| 261 | T::regs().cr2().modify(|reg| { | 312 | T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); |
| 262 | reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); | ||
| 263 | }); | ||
| 264 | 313 | ||
| 265 | let val = self.convert(); | 314 | // Configure channel |
| 315 | Self::set_channel_sample_time(channel, self.sample_time); | ||
| 266 | 316 | ||
| 267 | // dissable ADC | 317 | let val = self.convert(); |
| 268 | T::regs().cr2().modify(|reg| { | ||
| 269 | reg.set_swstart(false); | ||
| 270 | }); | ||
| 271 | T::regs().cr2().modify(|reg| { | ||
| 272 | reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); | ||
| 273 | }); | ||
| 274 | 318 | ||
| 275 | val | 319 | val |
| 276 | } | ||
| 277 | } | 320 | } |
| 278 | 321 | ||
| 279 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 322 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { |
| @@ -288,3 +331,9 @@ where | |||
| 288 | } | 331 | } |
| 289 | } | 332 | } |
| 290 | } | 333 | } |
| 334 | |||
| 335 | impl<'d, T: Instance> Drop for Adc<'d, T> { | ||
| 336 | fn drop(&mut self) { | ||
| 337 | T::disable(); | ||
| 338 | } | ||
| 339 | } | ||
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index d356d7b66..eda2b2a72 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; | |||
| 5 | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; | 5 | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; |
| 6 | use pac::adccommon::vals::Presc; | 6 | use pac::adccommon::vals::Presc; |
| 7 | 7 | ||
| 8 | use super::{AdcPin, Instance}; | 8 | use super::{AdcPin, Instance, InternalChannel}; |
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 10 | use crate::{pac, Peripheral}; | 10 | use crate::{pac, Peripheral}; |
| 11 | 11 | ||
| @@ -50,18 +50,10 @@ impl Resolution { | |||
| 50 | } | 50 | } |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | pub trait InternalChannel<T>: sealed::InternalChannel<T> {} | ||
| 54 | |||
| 55 | mod sealed { | ||
| 56 | pub trait InternalChannel<T> { | ||
| 57 | fn channel(&self) -> u8; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | // 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 | 53 | // 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 |
| 62 | pub struct VrefInt; | 54 | pub struct VrefInt; |
| 63 | impl<T: Instance> InternalChannel<T> for VrefInt {} | 55 | impl<T: Instance> InternalChannel<T> for VrefInt {} |
| 64 | impl<T: Instance> sealed::InternalChannel<T> for VrefInt { | 56 | impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { |
| 65 | fn channel(&self) -> u8 { | 57 | fn channel(&self) -> u8 { |
| 66 | 19 | 58 | 19 |
| 67 | } | 59 | } |
| @@ -69,7 +61,7 @@ impl<T: Instance> sealed::InternalChannel<T> for VrefInt { | |||
| 69 | 61 | ||
| 70 | pub struct Temperature; | 62 | pub struct Temperature; |
| 71 | impl<T: Instance> InternalChannel<T> for Temperature {} | 63 | impl<T: Instance> InternalChannel<T> for Temperature {} |
| 72 | impl<T: Instance> sealed::InternalChannel<T> for Temperature { | 64 | impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { |
| 73 | fn channel(&self) -> u8 { | 65 | fn channel(&self) -> u8 { |
| 74 | 18 | 66 | 18 |
| 75 | } | 67 | } |
| @@ -77,7 +69,7 @@ impl<T: Instance> sealed::InternalChannel<T> for Temperature { | |||
| 77 | 69 | ||
| 78 | pub struct Vbat; | 70 | pub struct Vbat; |
| 79 | impl<T: Instance> InternalChannel<T> for Vbat {} | 71 | impl<T: Instance> InternalChannel<T> for Vbat {} |
| 80 | impl<T: Instance> sealed::InternalChannel<T> for Vbat { | 72 | impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { |
| 81 | fn channel(&self) -> u8 { | 73 | fn channel(&self) -> u8 { |
| 82 | // TODO this should be 14 for H7a/b/35 | 74 | // TODO this should be 14 for H7a/b/35 |
| 83 | 17 | 75 | 17 |
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 871185074..1d030f7dc 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -2,9 +2,10 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; | ||
| 5 | use defmt::*; | 6 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::adc::Adc; | 8 | use embassy_stm32::adc::{Adc, Temperature, VrefInt}; |
| 8 | use embassy_time::{Delay, Duration, Timer}; | 9 | use embassy_time::{Delay, Duration, Timer}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 11 | ||
| @@ -13,12 +14,30 @@ async fn main(_spawner: Spawner) { | |||
| 13 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 14 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 15 | 16 | ||
| 16 | let mut adc = Adc::new(p.ADC1, &mut Delay); | 17 | let mut delay = Delay; |
| 18 | let mut adc = Adc::new(p.ADC1, &mut delay); | ||
| 17 | let mut pin = p.PC1; | 19 | let mut pin = p.PC1; |
| 18 | 20 | ||
| 21 | let mut vrefint = adc.enable_vrefint(); | ||
| 22 | let mut temp = adc.enable_temperature(); | ||
| 23 | |||
| 24 | // Startup delay can be combined to the maximum of either | ||
| 25 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); | ||
| 26 | |||
| 19 | loop { | 27 | loop { |
| 28 | // Read pin | ||
| 20 | let v = adc.read(&mut pin); | 29 | let v = adc.read(&mut pin); |
| 21 | info!("--> {} - {} mV", v, adc.to_millivolts(v)); | 30 | info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); |
| 31 | |||
| 32 | // Read internal temperature | ||
| 33 | let v = adc.read_internal(&mut temp); | ||
| 34 | let celcius = Temperature::to_celcius(adc.to_millivolts(v)); | ||
| 35 | info!("Internal temp: {} ({} C)", v, celcius); | ||
| 36 | |||
| 37 | // Read internal voltage reference | ||
| 38 | let v = adc.read_internal(&mut vrefint); | ||
| 39 | info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); | ||
| 40 | |||
| 22 | Timer::after(Duration::from_millis(100)).await; | 41 | Timer::after(Duration::from_millis(100)).await; |
| 23 | } | 42 | } |
| 24 | } | 43 | } |
