aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-10-10 06:28:41 +0000
committerGitHub <[email protected]>2022-10-10 06:28:41 +0000
commitef533e6df48d35b57fcb497e7bb5d6bafd6ca725 (patch)
tree44d6dfa425e60deb6dd28eb738563b5857d75719
parentf8fd6ab208fd09142ab9789078e9b23ba8c3e6a9 (diff)
parent322cfafed353ddfbe62121238ef97c56dd7a6eed (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.rs9
-rw-r--r--embassy-stm32/src/adc/v2.rs161
-rw-r--r--embassy-stm32/src/adc/v4.rs16
-rw-r--r--examples/stm32f4/src/bin/adc.rs25
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)))]
34pub trait Instance: sealed::Instance + 'static {} 38pub trait Instance: sealed::Instance + 'static {}
35#[cfg(adc_f1)] 39#[cfg(any(adc_f1, adc_v2))]
36pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} 40pub 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)))]
38pub trait Common: sealed::Common + 'static {} 42pub trait Common: sealed::Common + 'static {}
39pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} 43pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
44pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
40 45
41#[cfg(not(stm32h7))] 46#[cfg(not(stm32h7))]
42foreach_peripheral!( 47foreach_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;
3use embassy_hal_common::into_ref; 3use embassy_hal_common::into_ref;
4use embedded_hal_02::blocking::delay::DelayUs; 4use embedded_hal_02::blocking::delay::DelayUs;
5 5
6use super::InternalChannel;
6use crate::adc::{AdcPin, Instance}; 7use crate::adc::{AdcPin, Instance};
8use crate::peripherals::ADC1;
7use crate::time::Hertz; 9use crate::time::Hertz;
8use crate::Peripheral; 10use 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.
13pub const VREF_CALIB_MV: u32 = 3300; 15pub const VREF_CALIB_MV: u32 = 3300;
14 16
15#[cfg(not(any(rcc_f4, rcc_f7)))] 17/// ADC turn-on time
16fn enable() { 18pub const ADC_POWERUP_TIME_US: u32 = 3;
17 todo!()
18}
19
20#[cfg(any(rcc_f4, rcc_f7))]
21fn 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
30pub enum Resolution { 20pub enum Resolution {
31 TwelveBit, 21 TwelveBit,
@@ -61,24 +51,53 @@ impl Resolution {
61} 51}
62 52
63pub struct VrefInt; 53pub struct VrefInt;
64impl<T: Instance> AdcPin<T> for VrefInt {} 54impl InternalChannel<ADC1> for VrefInt {}
65impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { 55impl 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
61impl VrefInt {
62 /// Time needed for internal voltage reference to stabilize
63 pub fn start_time_us() -> u32 {
64 10
65 }
66}
67
71pub struct Temperature; 68pub struct Temperature;
72impl<T: Instance> AdcPin<T> for Temperature {} 69impl InternalChannel<ADC1> for Temperature {}
73impl<T: Instance> super::sealed::AdcPin<T> for Temperature { 70impl 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
82impl 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
79pub struct Vbat; 98pub struct Vbat;
80impl<T: Instance> AdcPin<T> for Vbat {} 99impl InternalChannel<ADC1> for Vbat {}
81impl<T: Instance> super::sealed::AdcPin<T> for Vbat { 100impl 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
335impl<'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;
5use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; 5use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
6use pac::adccommon::vals::Presc; 6use pac::adccommon::vals::Presc;
7 7
8use super::{AdcPin, Instance}; 8use super::{AdcPin, Instance, InternalChannel};
9use crate::time::Hertz; 9use crate::time::Hertz;
10use crate::{pac, Peripheral}; 10use crate::{pac, Peripheral};
11 11
@@ -50,18 +50,10 @@ impl Resolution {
50 } 50 }
51} 51}
52 52
53pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
54
55mod 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
62pub struct VrefInt; 54pub struct VrefInt;
63impl<T: Instance> InternalChannel<T> for VrefInt {} 55impl<T: Instance> InternalChannel<T> for VrefInt {}
64impl<T: Instance> sealed::InternalChannel<T> for VrefInt { 56impl<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
70pub struct Temperature; 62pub struct Temperature;
71impl<T: Instance> InternalChannel<T> for Temperature {} 63impl<T: Instance> InternalChannel<T> for Temperature {}
72impl<T: Instance> sealed::InternalChannel<T> for Temperature { 64impl<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
78pub struct Vbat; 70pub struct Vbat;
79impl<T: Instance> InternalChannel<T> for Vbat {} 71impl<T: Instance> InternalChannel<T> for Vbat {}
80impl<T: Instance> sealed::InternalChannel<T> for Vbat { 72impl<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
5use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
5use defmt::*; 6use defmt::*;
6use embassy_executor::Spawner; 7use embassy_executor::Spawner;
7use embassy_stm32::adc::Adc; 8use embassy_stm32::adc::{Adc, Temperature, VrefInt};
8use embassy_time::{Delay, Duration, Timer}; 9use embassy_time::{Delay, Duration, Timer};
9use {defmt_rtt as _, panic_probe as _}; 10use {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}