Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mcu temperature sensor #163

Open
pdgilbert opened this issue Oct 29, 2020 · 3 comments
Open

mcu temperature sensor #163

pdgilbert opened this issue Oct 29, 2020 · 3 comments
Labels
question Further information is requested

Comments

@pdgilbert
Copy link

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 structure Temperature 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.

@Sh3Rm4n Sh3Rm4n added the question Further information is requested label Oct 29, 2020
@David-OConnor
Copy link
Contributor

David-OConnor commented Nov 1, 2020

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.

@pdgilbert
Copy link
Author

@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
        }

}

@David-OConnor
Copy link
Contributor

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants