Skip to content

Commit 45e1163

Browse files
authored
Merge pull request #63 from stm32-rs/feature/absolute-adc
ADC absolute voltage measurement
2 parents 8d379e4 + 62ffce4 commit 45e1163

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

examples/adc.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extern crate panic_halt;
99
extern crate stm32g0xx_hal as hal;
1010

1111
use cortex_m_semihosting::hprintln;
12-
use hal::analog::adc::{Precision, SampleTime, VTemp};
12+
use hal::analog::adc::{OversamplingRatio, Precision, SampleTime, VTemp};
1313
use hal::prelude::*;
1414
use hal::stm32;
1515
use rt::entry;
@@ -26,19 +26,23 @@ fn main() -> ! {
2626
let mut adc = dp.ADC.constrain(&mut rcc);
2727
adc.set_sample_time(SampleTime::T_80);
2828
adc.set_precision(Precision::B_12);
29+
adc.set_oversamling_ratio(OversamplingRatio::X_16);
30+
adc.set_oversamling_shift(16);
31+
adc.oversamling_enable(true);
32+
2933
delay.delay(20.us()); // Wait for ADC voltage regulator to stabilize
3034
adc.calibrate();
3135

3236
let mut adc_pin = gpioa.pa0.into_analog();
37+
3338
let mut vtemp = VTemp::new();
3439
vtemp.enable(&mut adc);
3540

3641
loop {
37-
let u_raw: u32 = adc.read(&mut adc_pin).expect("adc read failed");
3842
let temp: u32 = adc.read(&mut vtemp).expect("temperature read failed");
43+
let u_raw: u32 = adc.read(&mut adc_pin).expect("adc read failed");
44+
let u_mv = adc.read_voltage(&mut adc_pin).expect("adc read failed");
3945

40-
let u = u_raw.saturating_sub(32) as f32 / 4_096_f32 * 3.3;
41-
let temp = temp / 42;
42-
hprintln!("u: {:.2} V | t: {:?}°C", u, temp).unwrap();
46+
hprintln!("U raw: {} | U: {} mV | t: {}°C", u_raw, u_mv, temp / 42).unwrap();
4347
}
4448
}

src/analog/adc.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
//! # Analog to Digital converter
2+
use core::ptr;
3+
24
use crate::gpio::*;
35
use crate::rcc::Rcc;
46
use crate::stm32::ADC;
@@ -104,6 +106,7 @@ pub struct Adc {
104106
sample_time: SampleTime,
105107
align: Align,
106108
precision: Precision,
109+
vdda_mv: Option<u32>,
107110
}
108111

109112
/// Contains the calibration factors for the ADC which can be reused with [`Adc::set_calibration()`]
@@ -121,6 +124,7 @@ impl Adc {
121124
sample_time: SampleTime::T_2,
122125
align: Align::Right,
123126
precision: Precision::B_12,
127+
vdda_mv: None,
124128
}
125129
}
126130

@@ -223,6 +227,41 @@ impl Adc {
223227
self.rb.ier.modify(|_, w| w.eocie().clear_bit()); // end of sequence interupt disable
224228
}
225229

230+
pub fn read_voltage<PIN: Channel<Adc, ID = u8>>(
231+
&mut self,
232+
pin: &mut PIN,
233+
) -> nb::Result<u16, ()> {
234+
let vdda_mv = if let Some(vdda_mv) = self.vdda_mv {
235+
vdda_mv
236+
} else {
237+
let mut vref = VRef::new();
238+
let vref_val: u32 = if vref.enabled(self) {
239+
self.read(&mut vref)?
240+
} else {
241+
vref.enable(self);
242+
let vref_val = self.read(&mut vref)?;
243+
vref.disable(self);
244+
vref_val
245+
};
246+
247+
let vref_cal: u32 = unsafe {
248+
// DS12766 3.13.2
249+
ptr::read_volatile(0x1FFF_75AA as *const u16) as u32
250+
};
251+
252+
// RM0454 14.9 Calculating the actual VDDA voltage using the internal reference voltage
253+
// V_DDA = 3 V x VREFINT_CAL / VREFINT_DATA
254+
let vdda_mv = vref_cal * 3_000_u32 / vref_val;
255+
self.vdda_mv = Some(vdda_mv);
256+
vdda_mv
257+
};
258+
259+
self.read(pin).map(|raw: u32| {
260+
let adc_mv = (vdda_mv * raw) >> 12;
261+
adc_mv as u16
262+
})
263+
}
264+
226265
pub fn release(self) -> ADC {
227266
self.rb
228267
}
@@ -375,6 +414,10 @@ macro_rules! int_adc {
375414
pub fn disable(&mut self, adc: &mut Adc) {
376415
adc.rb.ccr.modify(|_, w| w.$en().clear_bit());
377416
}
417+
418+
pub fn enabled(&self, adc: &Adc) -> bool {
419+
adc.rb.ccr.read().$en().bit_is_set()
420+
}
378421
}
379422

380423
impl Default for $Chan {
@@ -394,6 +437,12 @@ macro_rules! int_adc {
394437
};
395438
}
396439

440+
int_adc! {
441+
VTemp: (12, tsen),
442+
VRef: (13, vrefen),
443+
VBat: (14, vbaten),
444+
}
445+
397446
macro_rules! adc_pin {
398447
($($Chan:ty: ($pin:ty, $chan:expr)),+ $(,)*) => {
399448
$(
@@ -406,12 +455,6 @@ macro_rules! adc_pin {
406455
};
407456
}
408457

409-
int_adc! {
410-
VTemp: (12, tsen),
411-
VRef: (13, vrefen),
412-
VBat: (14, vbaten),
413-
}
414-
415458
adc_pin! {
416459
Channel0: (gpioa::PA0<Analog>, 0u8),
417460
Channel1: (gpioa::PA1<Analog>, 1u8),

0 commit comments

Comments
 (0)