Open pdgilbert opened 4 years ago
From the Reference manual, section 15.3.30: The temperature sensor is internally connected to the ADC1_IN16 input channel which is used to convert the sensor’s output voltage to a digital value. . So, what you said applies broadly to the 303 family.
The instructions, from the RM:
Reading the temperature
To use the sensor:
1. Select the ADC1_IN16 input channel (with the appropriate sampling time).
2. Program with the appropriate sampling time (refer to electrical characteristics section of
the STM32F3xx datasheet).
3. Set the TSEN bit in the ADC1_CCR register to wake up the temperature sensor from
power-down mode.
4. Start the ADC conversion.
5. Read the resulting VTS data in the ADC data register.
let vts =
6. Calculate the actual temperature using the following formula:
Temperature (in °C) = {(V25 – VTS) / Avg_Slope} + 25
Where:
– V25 = VTS value for 25° C
– Avg_Slope = average slope of the temperature vs. VTS curve (given in mV/°C or
µV/°C)
From the reference manual, we find V25 is typically 1.43, and avg slope is 4.3. Minimum sampling time is 2.2µs.
This gives a reasonable reading on my end, but needs a qc.
/// Check the temperature of the MCU. Output in voltage.
/// See reference manual, 15.3.30
pub fn read_mcu_temp(adc1_2: &mut pac::ADC1_2, adc: &mut pac::ADC1) -> f32 {
// 1. Select the ADC1_IN16 input channel (with the appropriate sampling time).
adc.cr.modify(|_, w| w.aden().enable());
// while adc.isr.read().adrdy().is_not_ready() {}
adc.sqr1.modify(|_, w|
unsafe { w.sq1().bits(16) }
);
// 2. Program with the appropriate sampling time (refer to electrical characteristics section of
// the STM32F3xx datasheet). Required sampling time: >= 2.2μs. Given common ADC
// frequencies in the 12 - 48Mhz range, using the minimum sample time of 1.5 clock
// cycles is adqeuate.
adc.smpr2.modify(|_, w| w.smp16().bits(0b000));
// 3. Set the TSEN bit in the ADC1_CCR register to wake up the temperature sensor from
// power-down mode.
adc1_2.ccr.modify(|_, w| w.tsen().enabled());
// 4. Start the ADC conversion.
adc.cr.modify(|_, w| w.adstart().start());
// while adc.isr.read().eos().is_not_complete() {}
adc.isr.modify(|_, w| w.eos().clear());
// 5. Read the resulting VTS data in the ADC data register.
let vts = adc.dr.read().rdata().bits();
// 6. Calculate the actual temperature using the following formula:
// Temperature (in °C) = {(V25 – VTS) / Avg_Slope} + 25
// Where:
// – V25 = VTS value for 25° C
// – Avg_Slope = average slope of the temperature vs. VTS curve (given in mV/°C or
// µV/°C)
((1.43 - vts as f32) / 4.3) + 25.
}
To call it:
let t = read_mcu_temp(&mut dp.ADC1_2, &mut dp.ADC1);
You may be able to use the existing adc
module, but I'm not sure if it's willing to share the tsen
register, and don't know how to set channels; it appears it sets channel by the input pin you pass to it, but there's no pin associated with channel 16.
If this works for you, please submit it as a PR.
@David-OConnor I'm a newbie so may be doing something simple wrong, but when I try your code in a loop I get 25.332558 C and no sensitivity to an increase if I put my finger on the processor to warm it up. I changed the code as below and see that the vts value is always zero. (I also had to remove some characters in comments and strings that the compiler did not like on my system. I don't think I changed any code.) Any idea what I'm doing wrong, or what I should try to debug it?
// #![deny(unsafe_code)]
#![no_main]
#![no_std]
use panic_semihosting as _;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
use asm_delay::{ AsmDelay, bitrate, };
use embedded_hal::blocking::delay::DelayMs;
#[cfg(feature = "stm32f3xx")] // eg Discovery-stm32f303
use stm32f3xx_hal::{//prelude::*,
pac,
pac::Peripherals,
};
#[cfg(feature = "stm32f3xx")] // eg Discovery-stm32f303
pub fn read_mcu_temp(adc1_2: &mut pac::ADC1_2, adc: &mut pac::ADC1) -> (f32, u16) {
// from https://github.com/stm32-rs/stm32f3xx-hal/issues/163 (David OConnor)
// Check the temperature of the MCU. Output in voltage.
// See reference manual, 15.3.30
// 1. Select the ADC1_IN16 input channel (with the appropriate sampling time).
adc.cr.modify(|_, w| w.aden().enable());
// while adc.isr.read().adrdy().is_not_ready() {}
adc.sqr1.modify(|_, w|
unsafe { w.sq1().bits(16) }
);
// 2. Program with the appropriate sampling time (refer to electrical characteristics section of
// the STM32F3xx datasheet). Required sampling time: >= 2.2. Given common ADC
// frequencies in the 12 - 48Mhz range, using the minimum sample time of 1.5 clock
// cycles is adqeuate.
adc.smpr2.modify(|_, w| w.smp16().bits(0b000));
// 3. Set the TSEN bit in the ADC1_CCR register to wake up the temperature sensor from
// power-down mode.
adc1_2.ccr.modify(|_, w| w.tsen().enabled());
// 4. Start the ADC conversion.
adc.cr.modify(|_, w| w.adstart().start());
// while adc.isr.read().eos().is_not_complete() {}
adc.isr.modify(|_, w| w.eos().clear());
// 5. Read the resulting VTS data in the ADC data register.
let vts = adc.dr.read().rdata().bits();
// 6. Calculate the actual temperature using the following formula:
// Temperature (in deg C) = {(V25 VTS) / Avg_Slope} + 25
// Where:
// V25 = VTS value for 25 C
// Avg_Slope = average slope of the temperature vs. VTS curve (given in mV/ deg C or
// microV/ deg C)
(((1.43 - vts as f32) / 4.3) + 25. , vts)
}
#[entry]
fn main() -> ! {
let mut dp = Peripherals::take().unwrap();
let mut delay = AsmDelay::new(bitrate::U32BitrateExt::mhz(16));
loop {
let (t, v) = read_mcu_temp(&mut dp.ADC1_2, &mut dp.ADC1);
hprintln!(" MCU temp: {} C {} mV", t, v).unwrap();
delay.delay_ms(3_000u32); // 10_000u32 should = 10s for timer checking
}
}
I probably missed one or more steps in ADC setup. The fact I had to comment out those while loops should have been the red flag!
There does not seem to be any way to connect to the mcu temperature sensor. (On Discovery-stm32f303 I think this uses an internal connection to ADC1 on channel 16,) In stm32f1xx-hal this is done by having a special method
.read_temp()
and in stm32f4xx-hal it is done by having a structureTemperature
which takes the place of a channel attached to a pin in method.read(&mut Temperature)
, but I don't see any mechanism in stm32f3xx-hal.