STM32H743 Internal Temperature Measurement discrepancy with measured external temperature
I am noticing a particular issue while measuring the internal temperature sensor using ADC on an STM32H743. Since my Vref tends to vary with temperature, I am internally sampling the Vref to measure the correct reference to the temperature sensing unit.
In the main codeblock that calculates the temperature from the ADC values, I am using the built-in calibrated values for 30C and 110C. Please see below.
My ADC is setup in the following way.
static void MX_ADC3_Init(void)
{
hadc3.Instance = ADC3;
hadc3.Init.Resolution = ADC_RESOLUTION_16B;
hadc3.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc3.Init.LowPowerAutoWait = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4; // 64 MHz / 4 = 16 MHz
// --- CRITICAL POLLING SEQUENCE CORRECTIONS ---
hadc3.Init.NbrOfConversion = 2;
hadc3.Init.DiscontinuousConvMode = ENABLE/*DISABLE*/; // Pauses sequence after each rank
hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
hadc3.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN/*ADC_OVR_DATA_PRESERVED*/;
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc3.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
// NEW: Rank 1 = internal voltage reference (used to compute actual VDDA)
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
sConfig.OffsetSignedSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler();
}
// NEW: Rank 2 = internal temperature sensor
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
sConfig.OffsetSignedSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler();
}
// NEW: ADC offset/linearity calibration (was never performed). Must be done after init/config, before conversions.
if (HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET_LINEARITY, ADC_SINGLE_ENDED) != HAL_OK)
{
Error_Handler();
}
}
In my main loop, I am using the following codeblock.
mcu_adc_ok = 0;
vrefint_data = 0;
temp_adc_value = 0;
// Read Rank 1
if (HAL_ADC_Start(&hadc3) == HAL_OK)
{
if (HAL_ADC_PollForConversion(&hadc3, 10) == HAL_OK)
{
vrefint_data = HAL_ADC_GetValue(&hadc3); // Grabs Rank 1, clears EOC
hadc3.Instance->CR |= ADC_CR_ADSTART; // Read Rank 2
if (HAL_ADC_PollForConversion(&hadc3, 10) == HAL_OK)
{
temp_adc_value = HAL_ADC_GetValue(&hadc3); // Grabs Rank 2, clears EOC
mcu_adc_ok = 1;
}
}
HAL_ADC_Stop(&hadc3);
}
if (mcu_adc_ok && (vrefint_data != 0))
{
float ts_cal1 = (float)(*TEMPSENSOR_CAL1_ADDR); // raw @ 30C, VDDA = 3.3V
float ts_cal2 = (float)(*TEMPSENSOR_CAL2_ADDR); // raw @ 110C, VDDA = 3.3V
float vcal = (float)(*VREFINT_CAL_ADDR); // raw VREFINT @ 3.3V
float ts_comp;
MCU_vdda = 3.3f * vcal / (float)vrefint_data; // measured VDDA in volts
if ((MCU_vdda < 2.7f) || (MCU_vdda > 3.6f)) mcu_adc_ok = 0; // sanity check
else
{
ts_comp = (float)temp_adc_value * MCU_vdda / 3.3f; // temp sensor counts normalized to VDDA = 3.3V
MCU_temp = ((ts_comp - ts_cal1) * (float)(TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP))
/ (ts_cal2 - ts_cal1) + (float)TEMPSENSOR_CAL1_TEMP;
}
}
else
mcu_adc_ok = 0;
To test this setup, I have placed the circuit board in an environment chamber at temperatures ranging from -30C to 50C. As a control, I put a thermocouple on the surface of the STM32.
I am attaching a plot of Measured MCU temperature (listed as InnerBall on the X axis) vs Thermocouple (TC) temperature. (At the time of post, this window does not let me paste the image here or attach a file here. )
Thermocouple_temperature = 1.2*MCU_Temperature -10.8C.
Question: I can believe that there is an offset between the meaured temperature on the outside of the STM and the internal silicon junction temperature. But why is there a gain (1.2) between the measured internal temperature and the measured outside skin temperature of this STM.
Thanks in advance.
Notes: This result is consistent across measuring 5 different units.
