š”ļø Get temperature from STM32 internal temperature sensor (simple library)
STM32 microcontroller has internal temperature sensor. This sensor is pretty roughly (±1.5 °C) and internally connected to on of ADC-channel, that makes possible easy get device temperature, that can be used without any additional components.
How it works?
1) Configure ADC to get values from both Temperature Sensor andVrefInt channel.
2) Get real VDDA (in my case it's equal to VREF+) voltage with VrefInt channel value (it will be around 3.3V for BlackPill). and then sensor's voltage (it will be around 0.76V at 25 °C).
3) Calculate temperature value with equation given in the Reference Manual:
Constant value given in the Datasheet:
Formula and constants
So, now we have:
Temperature = ((Vsense ā V25)/Avg_Slope) + 25.0Ā
Where:
TemperatureĀ āĀ calculated sensor temperature [°C];
VsenseĀ āĀ measuredĀ sensor's voltage [V];
V25Ā āĀ sensor's voltage at 25 °C [0.76V];
Avg_Slope ā temperature coefficient (voltage per degree)Ā [2.5mV/°C = 0.0025V/°C];
All we need, that measure sensor's voltage and find temperature by equation above!
STM32CubeIDE Project
1) Create new project:Ā New -> STM32 Project
2) Choose microcontroller unit (note, that F0xx series may not have internal sensor): STM32F401CCU6
3) Type any project name:
Ā Cube Configuration
1) Enable debugger:Ā SYS -> Debug: Serial Wire
2) Enable external crystal: RCC -> HSE: Crystal/Ceramic Resonator
3) Set input frequency (typically on Black Board mounted 25MHZ crystal), type HCLK 84 MHz and press Enter, wait some time for resolving.
4) Select Temperature Sensor Channel and Vrefint Channel (the second one is optional, it makes possible to know real chip supply voltage):
5) Add new DMA request from ADC, direction must be from peripheral (ADC) to memory (RAM region), circular mode and Half WordĀ (uint16_t) data width.
6) Enable interrupt from ADC, that help us to know, then conversion is completed:
7) Enable Scan Conversion Mode,Ā Continuous Conversion Mode and DMA Continuous Mode. Set two (Tmp CH and Ref CH) channels, the first should be Vrefint and the second is Sensor (can be in the order of your convenience)Ā set maximum sampling time and Timer 3 Trigger Out event (this timer will start conversion):
8) Select Internal Clock for TIM3 and setĀ timer frequency, in my case it will be:
APB/((PSC+1)(ARR+1)) =Ā 84000000/((840-1+1)(10000-1+1)) = 10 Hz
*APB1/APB2 frequency, depending on using timer (in my case both peripheral bus have the same frequency)
Thus, conversion will be turned 10 times per second.
Programming
I make a simple library, just add include and source files in the previously created project (uart_printfĀ not necessarily)
1) Include libraries
/* USER CODE BEGIN Includes */ #include "tmpsensor.h" #include "uart_printf.h" /* USER CODE END Includes */
2) Create uint16_t (Half Word)Ā array for raw data from ADC, double variable for temperature and another one variable for conversion completion flag. I do this with struct and typedef:
/* USER CODE BEGIN PV */ typedef struct AdcValues{ uint16_t Raw[2]; /* Raw values from ADC */ double IntSensTmp; /* Temperature */ }adcval_t; adcval_t Adc; typedef struct Flags { uint8_t ADCCMPLT; }flag_t; flag_t Flg = {0, }; /* USER CODE END PV */
4) Create macros, that help enable/disable all serial outputs:
/* USER CODE BEGIN PD */ #define DBG_UART 1 /* USER CODE END PD */
5) Start ADC with DMA and then timer:
/* USER CODE BEGIN 2 */ HAL_ADC_Start_DMA(&hadc1, (uint32_t*)Adc.Raw, 2); HAL_TIM_Base_Start(&htim3); /* This timer starts ADC conversion */ /* USER CODE END 2 */
6) When conversion is completed set variable-flag to positive value (you can do this in callback function of in the interrupt, fileĀ stm32f4xx_it.c):
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if(hadc->Instance == ADC1) /* Check if the interrupt comes from ACD1 */ { /* Set flag to true */ Flg.ADCCMPLT = 255; } }
7)Ā Ā In the main loop check conversion completion flag, and if it true put raw data to function, that return calculated temperature:
/* USER CODE BEGIN WHILE */ while (1) { if (Flg.ADCCMPLT) /* Conversion completed, do calculations */ { /* Temperature Sensor ADC-value, Reference Voltage ADC-value (if use) */ Adc.IntSensTmp = TMPSENSOR_getTemperature(Adc.Raw[1], Adc.Raw[0]); #if(DBG_UART) /* Send data with UART2 */ UART_Printf_Dbg("Reference: Adc.Raw[0] = %u\r\n", Adc.Raw[0]); UART_Printf_Dbg("Sensor: Adc.Raw[1] = %u\r\n", Adc.Raw[1]); UART_Printf_Dbg("Temperature: Adc.IntSensTmp = %.2fā\r\n", Adc.IntSensTmp); #endif #if(DBG_UART) /* Delay */ HAL_Delay(250); #endif Flg.ADCCMPLT = 0; /* Nullify flag */ }/* USER CODE END WHILE */
Debug
1) Right-click on project ->Ā Build Project
Right-click on project ->Ā Debug As -> STM32 Cortex-M C/C++ Application:
2) Open Live VariablesĀ (Ctrl + 3 for search) and add global variables (in out case it's struct Adc.Ā Run program:
Open structure andĀ you will see data from ADC and calculated temperature value in Celsius degrees:
3) If you use full code (with UART_Printf...) also you can open serial terminal into STM32CubeIDEĀ and get debug data:
Full Project
Full tested project for STM32CubeIDEĀ you can get on GitHub:
ā”ļøĀ https://github.com/Egoruch/Internal-Temperature-Sensor-STM32-HAL
Cube Monitor
CubeIDE so slow and can't do graphs without SWO, so I recommend try STM32CubeMonitor for real-time graphs (instruction will be here...).
I added temperature variable and now it's displaying as a curve of time:
Stress test
The hot-air gun help us to test STM32 under high temperatur:
When chip temperature reaches 240°C-300°C data acquisition stops (apparently MCU resets?), and in a few seconds after heater removing work is being restored:
Video
Conclusion
Of course, this internal sensor can't be used for measurement air temperature, because it'sĀ related to thermal link between microchip and board. So, you can use it for data-logging device environment state or just show measurements on the screen:
- 3 comments