on 2024-07-30 06:00 AM
The STM32H7 MCU family clock reaches up to 600MHz and thermal management matters due to power dissipation, see details in the AN5036 Guidelines for thermal management on STM32 applications.
From an STM32 application point of view, the TJ temperature of the die (junction temperature) can be measured by an internal sensor. Notice that the internal sensor is not designed to acquire the temperature of surrounding air, due to various thermal resistances in between.
The temperature sensor is connected to one of the input channels of an ADC, for example, channel 18 of ADC3
Hint: On a Nucleo board, the VREF+ pin is connected to VDDA by default. Thus, VDDA acts as an ADC reference. To avoid hardware modification (short circuit of VREFBUF and VDDA otherwise), VREFINT connected to channel 19 of ADC3 is used to evaluate the accurate momentary value of VDDA. If a hardware limitation is not present on a given board, VRFBUF can be used as an ADC internal reference and no need for a VREFINT ADC channel to be acquired.
VREFINT = 1.216 V typ
It is possible to increase acquisition accuracy both for temperature sensor and VREFINT using calibration values. Given macros (stm32h7xx_LL_adc.h) are used to evaluate the precise value of temperature and VDDA.
__LL_ADC_CALC_TEMPERATURE()
__LL_ADC_CALC_VREFANALOG_VOLTAGE()
The sampling time for the temperature sensor analog input must be greater than the stabilization time specified in the product datasheet.
It is also relevant for VREFINT
We assume that you are familiar with STM32CubeMX and the configuration of STM32 peripherals.
1. Create project for STM32H7 board, for example, Nucleo using STM32CubeIDE or STM32CubeMX.
2. Configure USART connected to on-board STLINK virtual com port, for example, 115200,8,N,1.
3. Configure the ADC.
a. Select the ADC containing temperature sensor Channel and VREFINT channel, for example ADC3, enable channels.
b. Consider the ADC resolution, for example, 16-bit.
c. Consider the ADC core clock for example, 80MHz, set asynchronous clock mode divided by x, for example, x = 16 → 5MHz.
d. Set the number of conversion: 2 (2 channels to convert).
e. Enable continuous conversion mode.
f. Set external trigger conversion source: Regular conversion launched by software.
g. Configure the ADC DMA. Mode: Normal, half word, memory increment.
h. Set conversion data management mode: DMA one shot mode.
i. Configure rank 1: Channel VREFINT, sampling time = 64.5 → 1 /5 MHz * 64.5 = 12.9 μs → tS_vrefint = 4.3 μs.
j. Configure rank 2: Channel temperature sensor, sampling time = 64.5 = 12.9 μs → tS_temp = 9 μs.
4. Generate code (HAL library as default one for code generation).
1. Interquartile Mean (IQM) averaging algorithm is proposed since it allows skipping two quarters of extreme values (highest and lowest). Meanwhile, the remaining half of the collected batch is averaged, acting as a noise filter. Number of IQM samples: 64.
2. The proposed code below is relevant for main.c and STM32CubeMX code sections are followed (copy/paste section by section).
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE BEGIN PD */
#define CHNB (2)
#define BUFSIZE (64)
#define HBUFSIZE (BUFSIZE / 2)
#define QBUFSIZE (BUFSIZE / 4)
#define ADCBUFSIZE (CHNB * BUFSIZE)
/* USER CODE BEGIN PV */
uint16_t adcBuf[ADCBUFSIZE]; /* ADC buffer */
uint16_t adcChBuf[BUFSIZE]; /* ADC channel buffer */
uint32_t adcChAvg[CHNB]; /* ADC channel average value */
volatile uint32_t adcFlag = RESET; /* ADC DMA data ready flag */
uint16_t vDDA = 0; /* VDDA [mV] evaluated based on VREFINT ADC channel */
int16_t temperatureDegC = 0; /* TS temperature [DegC] */
uint16_t i,j,k;
/* USER CODE BEGIN PFP */
void bubblesort(uint16_t *array, uint16_t length);
/* USER CODE BEGIN 0 */
/* Redirection of printf to UART -> ST-Link VCP */
int _write(int fd, char * ptr, int len)
{
HAL_UART_Transmit(&huart3,(uint8_t *)ptr,len,HAL_MAX_DELAY);
return len;
}
/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc3,(uint32_t*)adcBuf,ADCBUFSIZE);
printf("Ready\r\n");
/* USER CODE BEGIN 3 */
HAL_Delay(1000);
if (adcFlag!=RESET)
{
adcFlag = RESET;
HAL_ADC_Stop_DMA(&hadc3);
/* InterQuartile Mean */
for(k=0;k<CHNB;k++)
{
j = 0; adcChAvg[k] = 0;
for(i=k;i<ADCBUFSIZE;i+=CHNB)
{
adcChBuf[j++] = adcBuf[i];
}
bubblesort(adcChBuf,BUFSIZE);
for (i=QBUFSIZE;i<(BUFSIZE-QBUFSIZE);i++)
{
adcChAvg[k] += adcChBuf[i];
}
adcChAvg[k] = adcChAvg[k] / HBUFSIZE;
}
vDDA = __LL_ADC_CALC_VREFANALOG_VOLTAGE(adcChAvg[0],LL_ADC_RESOLUTION_16B);
temperatureDegC = __LL_ADC_CALC_TEMPERATURE(vDDA,adcChAvg[1],LL_ADC_RESOLUTION_16B);
printf("%dmV | %dDegC\r\n",(int)vDDA,(int)temperatureDegC);
HAL_ADC_Start_DMA(&hadc3,(uint32_t*)adcBuf,ADCBUFSIZE);
}
/* USER CODE BEGIN 3 */
HAL_Delay(1000);
if (adcFlag!=RESET)
{
adcFlag = RESET;
HAL_ADC_Stop_DMA(&hadc3);
/* InterQuartile Mean */
for(k=0;k<CHNB;k++)
{
j = 0; adcChAvg[k] = 0;
for(i=k;i<ADCBUFSIZE;i+=CHNB)
{
adcChBuf[j++] = adcBuf[i];
}
bubblesort(adcChBuf,BUFSIZE);
for (i=QBUFSIZE;i<(BUFSIZE-QBUFSIZE);i++)
{
adcChAvg[k] += adcChBuf[i];
}
adcChAvg[k] = adcChAvg[k] / HBUFSIZE;
}
vDDA = __LL_ADC_CALC_VREFANALOG_VOLTAGE(adcChAvg[0],LL_ADC_RESOLUTION_16B);
temperatureDegC = __LL_ADC_CALC_TEMPERATURE(vDDA,adcChAvg[1],LL_ADC_RESOLUTION_16B);
printf("%dmV | %dDegC\r\n",(int)vDDA,(int)temperatureDegC);
HAL_ADC_Start_DMA(&hadc3,(uint32_t*)adcBuf,ADCBUFSIZE);
}
/* USER CODE BEGIN 4 */
void bubblesort(uint16_t *array, uint16_t length)
{
uint16_t i, j,tmp;
for (i = 1; i < length ; i++)
{
for (j = 0; j < length - i ; j++)
{
if (array[j] > array[j+1])
{
tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
adcFlag = SET;
}
An example of console output: VDDA [mV] followed by junction temperature [°]
NUCLEO-H743ZI2, MCU SYSCLK = 400MHz
3314mV | 37DegC
3314mV | 37DegC
3314mV | 37DegC
3314mV | 37DegC
3316mV | 37DegC
3315mV | 37DegC
3314mV | 37DegC