diff options
| author | chemicstry <[email protected]> | 2022-10-07 14:31:55 +0300 |
|---|---|---|
| committer | chemicstry <[email protected]> | 2022-10-07 14:31:55 +0300 |
| commit | df7174ecb03f466d4b97f2ce1a11e687317bd93a (patch) | |
| tree | bf3abbe1504a04f764e27dbf1e995b910477b483 | |
| parent | 9dca368c3dd1a8f00295b21c87d4fbb94afb60a5 (diff) | |
Fix internal channel reading on adc_v2
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v2.rs | 139 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v4.rs | 8 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/adc.rs | 25 |
4 files changed, 125 insertions, 52 deletions
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index fba016a77..0eb4eba73 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -28,6 +28,10 @@ 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(any(adc_f1, adc_v2)))] | 37 | #[cfg(not(any(adc_f1, adc_v2)))] |
| @@ -37,6 +41,7 @@ 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 70e3b73b3..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,6 +14,9 @@ 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 | ||
| 17 | /// ADC turn-on time | ||
| 18 | pub const ADC_POWERUP_TIME_US: u32 = 3; | ||
| 19 | |||
| 15 | pub enum Resolution { | 20 | pub enum Resolution { |
| 16 | TwelveBit, | 21 | TwelveBit, |
| 17 | TenBit, | 22 | TenBit, |
| @@ -46,24 +51,53 @@ impl Resolution { | |||
| 46 | } | 51 | } |
| 47 | 52 | ||
| 48 | pub struct VrefInt; | 53 | pub struct VrefInt; |
| 49 | impl<T: Instance> AdcPin<T> for VrefInt {} | 54 | impl InternalChannel<ADC1> for VrefInt {} |
| 50 | impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { | 55 | impl super::sealed::InternalChannel<ADC1> for VrefInt { |
| 51 | fn channel(&self) -> u8 { | 56 | fn channel(&self) -> u8 { |
| 52 | 17 | 57 | 17 |
| 53 | } | 58 | } |
| 54 | } | 59 | } |
| 55 | 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 | |||
| 56 | pub struct Temperature; | 68 | pub struct Temperature; |
| 57 | impl<T: Instance> AdcPin<T> for Temperature {} | 69 | impl InternalChannel<ADC1> for Temperature {} |
| 58 | impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | 70 | impl super::sealed::InternalChannel<ADC1> for Temperature { |
| 59 | fn channel(&self) -> u8 { | 71 | fn channel(&self) -> u8 { |
| 60 | 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 | ||
| 61 | } | 95 | } |
| 62 | } | 96 | } |
| 63 | 97 | ||
| 64 | pub struct Vbat; | 98 | pub struct Vbat; |
| 65 | impl<T: Instance> AdcPin<T> for Vbat {} | 99 | impl InternalChannel<ADC1> for Vbat {} |
| 66 | impl<T: Instance> super::sealed::AdcPin<T> for Vbat { | 100 | impl super::sealed::InternalChannel<ADC1> for Vbat { |
| 67 | fn channel(&self) -> u8 { | 101 | fn channel(&self) -> u8 { |
| 68 | 18 | 102 | 18 |
| 69 | } | 103 | } |
| @@ -152,19 +186,16 @@ where | |||
| 152 | T::enable(); | 186 | T::enable(); |
| 153 | T::reset(); | 187 | T::reset(); |
| 154 | 188 | ||
| 155 | let presc = unsafe { Prescaler::from_pclk2(T::frequency()) }; | 189 | let presc = Prescaler::from_pclk2(T::frequency()); |
| 156 | unsafe { | 190 | unsafe { |
| 157 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | 191 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); |
| 158 | } | ||
| 159 | 192 | ||
| 160 | unsafe { | ||
| 161 | // disable before config is set | ||
| 162 | T::regs().cr2().modify(|reg| { | 193 | T::regs().cr2().modify(|reg| { |
| 163 | reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); | 194 | reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); |
| 164 | }); | 195 | }); |
| 165 | } | 196 | } |
| 166 | 197 | ||
| 167 | delay.delay_us(20); // TODO? | 198 | delay.delay_us(ADC_POWERUP_TIME_US); |
| 168 | 199 | ||
| 169 | Self { | 200 | Self { |
| 170 | sample_time: Default::default(), | 201 | sample_time: Default::default(), |
| @@ -194,6 +225,45 @@ where | |||
| 194 | ((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 |
| 195 | } | 226 | } |
| 196 | 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 | |||
| 197 | /// Perform a single conversion. | 267 | /// Perform a single conversion. |
| 198 | fn convert(&mut self) -> u16 { | 268 | fn convert(&mut self) -> u16 { |
| 199 | unsafe { | 269 | unsafe { |
| @@ -224,42 +294,29 @@ where | |||
| 224 | P: crate::gpio::sealed::Pin, | 294 | P: crate::gpio::sealed::Pin, |
| 225 | { | 295 | { |
| 226 | unsafe { | 296 | unsafe { |
| 227 | // dissable ADC | ||
| 228 | T::regs().cr2().modify(|reg| { | ||
| 229 | reg.set_swstart(false); | ||
| 230 | }); | ||
| 231 | T::regs().cr2().modify(|reg| { | ||
| 232 | reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); | ||
| 233 | }); | ||
| 234 | |||
| 235 | pin.set_as_analog(); | 297 | pin.set_as_analog(); |
| 236 | 298 | ||
| 237 | // Configure ADC | 299 | self.read_channel(pin.channel()) |
| 238 | T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); | 300 | } |
| 301 | } | ||
| 239 | 302 | ||
| 240 | // Select channel | 303 | pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 { |
| 241 | T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); | 304 | unsafe { self.read_channel(channel.channel()) } |
| 305 | } | ||
| 242 | 306 | ||
| 243 | // Configure channel | 307 | unsafe fn read_channel(&mut self, channel: u8) -> u16 { |
| 244 | 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())); | ||
| 245 | 310 | ||
| 246 | // enable adc | 311 | // Select channel |
| 247 | T::regs().cr2().modify(|reg| { | 312 | T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); |
| 248 | reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); | ||
| 249 | }); | ||
| 250 | 313 | ||
| 251 | let val = self.convert(); | 314 | // Configure channel |
| 315 | Self::set_channel_sample_time(channel, self.sample_time); | ||
| 252 | 316 | ||
| 253 | // dissable ADC | 317 | let val = self.convert(); |
| 254 | T::regs().cr2().modify(|reg| { | ||
| 255 | reg.set_swstart(false); | ||
| 256 | }); | ||
| 257 | T::regs().cr2().modify(|reg| { | ||
| 258 | reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); | ||
| 259 | }); | ||
| 260 | 318 | ||
| 261 | val | 319 | val |
| 262 | } | ||
| 263 | } | 320 | } |
| 264 | 321 | ||
| 265 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 322 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index d356d7b66..7e6a219e1 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -50,14 +50,6 @@ 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 {} |
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 871185074..6f80c1ef1 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, SampleTime, 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 | } |
