Skip to content

Commit df8a6e5

Browse files
geomatsiTheZoq2
authored andcommitted
adc: add support for channels 16 and 17 in ADC1 (#67)
* adc: add support for channels 16 and 17 in ADC1 Add methods to read from ADC1 special channels: - channel 16: temperature sensor - channel 17: internal reference voltage Note that these methods should be implemented only for ADC1 block since in ADC2 and ADC3 those channels are connected to Vss. Signed-off-by: Sergey Matyukevich <[email protected]> * fixup: sampling time for temperature sensor Recommended ADC sampling time for temperature sensor is 17.1 usec. So use approximate sampling time settings to support the whole range of possible ADC frequencies. Signed-off-by: Sergey Matyukevich <[email protected]> * adc: examples Update existing ADC example according to ADC API changes. Add new ADC example to read ambient temperature using ADC1 CH16. Signed-off-by: Sergey Matyukevich <[email protected]> * fixup: remove redundant interrupt handlers Signed-off-by: Sergey Matyukevich <[email protected]> * fixup: remove extra use_hse examples from comments Signed-off-by: Sergey Matyukevich <[email protected]>
1 parent 5ad17fc commit df8a6e5

File tree

4 files changed

+133
-15
lines changed

4 files changed

+133
-15
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2727
- Remove all PWM channel configurations except 'all the channels for default remapping' configuratons
2828
- Update PWM documentation: clarify custom selection of channels
2929
- Add PWM example for custom selection of channels
30+
- Add ADC1 reading functions for channels 16 (temperature) and 17 (internal reference voltage)
31+
- Update existing ADC example according to ADC API changes
32+
- Add new ADC example to read ambient temperature using ADC1 CH16
3033

3134
### Changed
3235

examples/adc.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use stm32f1xx_hal::{
99
pac,
1010
adc,
1111
};
12-
use cortex_m_rt::{entry,exception,ExceptionFrame};
12+
use cortex_m_rt::entry;
1313

1414
use cortex_m_semihosting::hprintln;
1515

@@ -29,10 +29,10 @@ fn main() -> ! {
2929
hprintln!("adc freq: {}", clocks.adcclk().0).unwrap();
3030

3131
// Setup ADC
32-
let mut adc1 = adc::Adc::adc1(p.ADC1, &mut rcc.apb2);
32+
let mut adc1 = adc::Adc::adc1(p.ADC1, &mut rcc.apb2, clocks.adcclk());
3333

3434
#[cfg(feature = "stm32f103")]
35-
let mut adc2 = adc::Adc::adc2(p.ADC2, &mut rcc.apb2);
35+
let mut adc2 = adc::Adc::adc2(p.ADC2, &mut rcc.apb2, clocks.adcclk());
3636

3737
// Setup GPIOB
3838
let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
@@ -54,13 +54,3 @@ fn main() -> ! {
5454
}
5555
}
5656
}
57-
58-
#[exception]
59-
fn HardFault(ef: &ExceptionFrame) -> ! {
60-
panic!("{:#?}", ef);
61-
}
62-
63-
#[exception]
64-
fn DefaultHandler(irqn: i16) {
65-
panic!("Unhandled exception (IRQn = {})", irqn);
66-
}

examples/adc_temperature.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![deny(unsafe_code)]
2+
#![no_main]
3+
#![no_std]
4+
5+
use panic_semihosting as _;
6+
7+
use stm32f1xx_hal::{
8+
prelude::*,
9+
pac,
10+
adc,
11+
};
12+
use cortex_m_rt::entry;
13+
14+
use cortex_m_semihosting::hprintln;
15+
16+
#[entry]
17+
fn main() -> ! {
18+
// Aquire peripherals
19+
let p = pac::Peripherals::take().unwrap();
20+
let mut flash = p.FLASH.constrain();
21+
let mut rcc = p.RCC.constrain();
22+
23+
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(56.mhz()).pclk1(28.mhz()).adcclk(14.mhz()).freeze(&mut flash.acr);
24+
hprintln!("sysclk freq: {}", clocks.sysclk().0).unwrap();
25+
hprintln!("adc freq: {}", clocks.adcclk().0).unwrap();
26+
27+
// Setup ADC
28+
let mut adc = adc::Adc::adc1(p.ADC1, &mut rcc.apb2, clocks.adcclk());
29+
30+
// Read temperature sensor
31+
loop {
32+
let temp = adc.read_temp();
33+
34+
hprintln!("temp: {}", temp).unwrap();
35+
}
36+
}

src/adc.rs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use embedded_hal::adc::{Channel, OneShot};
55
use crate::gpio::Analog;
66
use crate::gpio::{gpioa, gpiob, gpioc};
77
use crate::rcc::APB2;
8+
use crate::time::Hertz;
89

910
use crate::stm32::ADC1;
1011
#[cfg(any(
@@ -17,6 +18,7 @@ pub struct Adc<ADC> {
1718
rb: ADC,
1819
sample_time: AdcSampleTime,
1920
align: AdcAlign,
21+
clk: Hertz
2022
}
2123

2224
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -171,11 +173,12 @@ macro_rules! adc_hal {
171173
///
172174
/// Sets all configurable parameters to one-shot defaults,
173175
/// performs a boot-time calibration.
174-
pub fn $init(adc: $ADC, apb2: &mut APB2) -> Self {
176+
pub fn $init(adc: $ADC, apb2: &mut APB2, clk: Hertz) -> Self {
175177
let mut s = Self {
176178
rb: adc,
177179
sample_time: AdcSampleTime::default(),
178180
align: AdcAlign::default(),
181+
clk: clk,
179182
};
180183
s.enable_clock(apb2);
181184
s.power_down();
@@ -332,7 +335,14 @@ macro_rules! adc_hal {
332335
.rb
333336
.smpr1
334337
.modify(|_, w| unsafe { w.smp15().bits(self.sample_time.into()) }),
335-
338+
16 => self
339+
.rb
340+
.smpr1
341+
.modify(|_, w| unsafe { w.smp16().bits(self.sample_time.into()) }),
342+
17 => self
343+
.rb
344+
.smpr1
345+
.modify(|_, w| unsafe { w.smp17().bits(self.sample_time.into()) }),
336346
_ => unreachable!(),
337347
}
338348

@@ -374,6 +384,85 @@ macro_rules! adc_hal {
374384
}
375385
}
376386

387+
impl Adc<ADC1> {
388+
fn read_aux(&mut self, chan: u8) -> u16 {
389+
let tsv_off = if self.rb.cr2.read().tsvrefe().bit_is_clear() {
390+
self.rb.cr2.modify(|_, w| w.tsvrefe().set_bit());
391+
true
392+
} else {
393+
false
394+
};
395+
396+
self.power_up();
397+
let val = self.convert(chan);
398+
self.power_down();
399+
400+
if tsv_off {
401+
self.rb.cr2.modify(|_, w| w.tsvrefe().clear_bit());
402+
}
403+
404+
val
405+
}
406+
407+
/// Temperature sensor is connected to channel 16 on ADC1. This sensor can be used
408+
/// to measure ambient temperature of the device. However note that the returned
409+
/// value is not an absolute temperature value.
410+
///
411+
/// In particular, according to section 11.10 from Reference Manual RM0008 Rev 20:
412+
/// "The temperature sensor output voltage changes linearly with temperature. The offset
413+
/// of this line varies from chip to chip due to process variation (up to 45 °C from one
414+
/// chip to another). The internal temperature sensor is more suited to applications
415+
/// that detect temperature variations instead of absolute temperatures. If accurate
416+
/// temperature readings are needed, an external temperature sensor part should be used."
417+
///
418+
/// Formula to calculate temperature value is also taken from the section 11.10.
419+
pub fn read_temp(&mut self) -> i32 {
420+
/// According to section 5.3.18 "Temperature sensor characteristics"
421+
/// from STM32F1xx datasheets, TS constants values are as follows:
422+
/// AVG_SLOPE - average slope
423+
/// V_25 - temperature sensor ADC voltage at 25°C
424+
const AVG_SLOPE: i32 = 43;
425+
const V_25: i32 = 1430;
426+
427+
let prev_cfg = self.save_cfg();
428+
429+
// recommended ADC sampling for temperature sensor is 17.1 usec,
430+
// so use the following approximate settings
431+
// to support all ADC frequencies
432+
let sample_time = match self.clk.0 {
433+
0 ... 1_200_000 => AdcSampleTime::T_1,
434+
1_200_001 ... 1_500_000 => AdcSampleTime::T_7,
435+
1_500_001 ... 2_400_000 => AdcSampleTime::T_13,
436+
2_400_001 ... 3_100_000 => AdcSampleTime::T_28,
437+
3_100_001 ... 4_000_000 => AdcSampleTime::T_41,
438+
4_000_001 ... 5_000_000 => AdcSampleTime::T_55,
439+
5_000_001 ... 10_000_000 => AdcSampleTime::T_71,
440+
_ => AdcSampleTime::T_239,
441+
};
442+
443+
self.set_sample_time(sample_time);
444+
let val_temp: i32 = self.read_aux(16u8).into();
445+
let val_vref: i32 = self.read_aux(17u8).into();
446+
let v_sense = val_temp * 1200 / val_vref;
447+
448+
self.restore_cfg(prev_cfg);
449+
450+
(V_25 - v_sense) * 10 / AVG_SLOPE + 25
451+
}
452+
453+
/// Internal reference voltage Vrefint is connected to channel 17 on ADC1.
454+
/// According to section 5.3.4 "Embedded reference voltage" from STM32F1xx
455+
/// datasheets, typical value of this reference voltage is 1200 mV.
456+
///
457+
/// This value is useful when ADC readings need to be converted into voltages.
458+
/// For instance, reading from any ADC channel can be converted into voltage (mV)
459+
/// using the following formula:
460+
/// v_chan = adc.read(chan) * 1200 / adc.read_vref()
461+
pub fn read_vref(&mut self) -> u16 {
462+
self.read_aux(17u8)
463+
}
464+
}
465+
377466
#[cfg(any(
378467
feature = "stm32f100",
379468
feature = "stm32f101",

0 commit comments

Comments
 (0)