2025-01-28 01:22 AM - edited 2025-01-28 01:24 AM
Hello everyone,
it was required that my software periodically measures the internal chip temperature for comparison with the externally measured temperature. As the time distances between measurements are several minutes, the ADC is to be completely turned off in between. I mostly copied the code from the LoRaWAN_End_Node example, but the measured temperature is always too low, around 5-6 °C when it should be around 20-21 °C.The ADC code is generally generated by CubeMX.
MCU is the STM32WLE5CCU.
I've pasted the code here:
void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_160CYCLES_5;
hadc.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_160CYCLES_5;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
uint32_t test_adc_readChannels(uint32_t channel)
{
uint32_t ADCxConvertedValues = 0;
ADC_ChannelConfTypeDef sConfig =
{ 0 };
MX_ADC_Init();
/* Start Calibration */
if (HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)
{
Error_Handler();
}
/* Configure Regular Channel */
sConfig.Channel = channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADC_Start(&hadc) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/** Wait for end of conversion */
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
/** Wait for end of conversion */
HAL_ADC_Stop(&hadc); /* it calls also ADC_Disable() */
ADCxConvertedValues = HAL_ADC_GetValue(&hadc);
HAL_ADC_DeInit(&hadc);
return ADCxConvertedValues;
}
uint16_t test_adc_measureBat(void)
{
uint16_t batteryLevelmV = 0;
uint32_t measuredLevel = 0;
measuredLevel = test_adc_readChannels(ADC_CHANNEL_VREFINT);
if (measuredLevel == 0)
{
batteryLevelmV = 0;
}
else
{
if ((uint32_t)*VREFINT_CAL_ADDR != (uint32_t)0xFFFFU)
{
batteryLevelmV = __LL_ADC_CALC_VREFANALOG_VOLTAGE(measuredLevel, ADC_RESOLUTION_12B);
}
else
{
batteryLevelmV = (VREFINT_CAL_VREF * 1510) / measuredLevel;
}
}
return batteryLevelmV;
}
int16_t test_adc_measureTemp(void)
{
__IO int16_t temperatureDegreeC = 0;
uint32_t measuredLevel = 0;
uint16_t batteryLevelmV = test_adc_measureBat();
measuredLevel = test_adc_readChannels(ADC_CHANNEL_TEMPSENSOR);
/* convert ADC level to temperature */
/* check whether device has temperature sensor calibrated in production */
if (((int32_t)*TEMPSENSOR_CAL2_ADDR - (int32_t)*TEMPSENSOR_CAL1_ADDR) != 0)
{
/* Device with temperature sensor calibrated in production:
use device optimized parameters */
temperatureDegreeC = __LL_ADC_CALC_TEMPERATURE(batteryLevelmV, measuredLevel, LL_ADC_RESOLUTION_12B);
}
else
{
/* Device with temperature sensor not calibrated in production:
use generic parameters */
temperatureDegreeC = __LL_ADC_CALC_TEMPERATURE_TYP_PARAMS(TEMPSENSOR_TYP_AVGSLOPE, TEMPSENSOR_TYP_CAL1_V,
TEMPSENSOR_CAL1_TEMP, batteryLevelmV, measuredLevel,
LL_ADC_RESOLUTION_12B);
}
/* from int16 to q8.7*/
temperatureDegreeC <<= 8;
return (int16_t)temperatureDegreeC;
}
I've tested with two different baords and had the same problem. The calibrated values for the temperature seem to be valid, and battery measurement seems to give the correct result.
Does anyone have any idea what goes wrong here? I've compared it dozens of times and cannot find the fault.
Solved! Go to Solution.
2025-01-28 06:31 AM - edited 2025-01-28 06:32 AM
In a STM32L4 project, I derived code from the formulae in th ref.man, and not using any library code,. as follows:
// raw 12-bit ADC data
printf("temp_raw_12bit: %4d; vref_raw_12bit: %4d; ", temp_raw_12bit, vref_raw_12bit);
// Calculating the actual VDDA
// factory calibration value, 12-bit raw ADC data
const int vref_cal_raw_12bit = *VREFINT_CAL_ADDR; // stored in engineering bytes area
// factory calibration was done at a fixed vdda of VREFINT_CAL_VREF mV
int vdda_mV = (vref_cal_raw_12bit * VREFINT_CAL_VREF) / vref_raw_12bit; // actual vdda in mV
// Calculating the temperature
int temp_mV = vdda_mV * temp_raw_12bit / 4095; // ADC measurement 12-bit raw --> mV
// factory calibration values, 12-bit raw ADC data.
// measured at two points:
// * 30°C (TEMPSENSOR_CAL1_TEMP)
// * 130°C (TEMPSENSOR_CAL2_TEMP)
const int ts_cal1_raw_12bit = *TEMPSENSOR_CAL1_ADDR; // measured at TEMPSENSOR_CAL1_TEMP °C
const int ts_cal2_raw_12bit = *TEMPSENSOR_CAL2_ADDR; // measured at TEMPSENSOR_CAL2_TEMP °C
// factory calibration was done at a fixed vdda of VREFINT_CAL_VREF mV
// convert the calibration values from 12-bit raw to mV:
int ts_cal1_mV = ts_cal1_raw_12bit * VREFINT_CAL_VREF / 4095;
int ts_cal2_mV = ts_cal2_raw_12bit * VREFINT_CAL_VREF / 4095;
// linear interpolation
int temp_degC = TEMPSENSOR_CAL1_TEMP + (TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP ) * (temp_mV - ts_cal1_mV) / (ts_cal2_mV - ts_cal1_mV);
// converted results
printf("vdda_mV: %4d; temp_degC: %4d\n", vdda_mV, temp_degC);
you may use this to get a second opinion.
hth
KnarfB
2025-01-28 01:40 AM
Did you check which if/else path is your code actively going?
hth
KnarfB
2025-01-28 01:46 AM
The program always goes to the first path, as the values are valid. I've actively redirected to the else path, but the value is still wrong. It seems the raw value from the ADC is already wrong.
For reference, CAL1 is 950 and CAL2 is 1264, VREFINTCAL is 1507 for my current MCU. The measured raw ADC value for the temperature is 1062.
2025-01-28 04:01 AM
I don't use your chip. Are you using the longest possible sampling time? Minimal sampling times should be in the data sheet.
hth
KnarfB
2025-01-28 04:11 AM
Yes, I do, the longest time is 160.5 cycles.
2025-01-28 04:30 AM - edited 2025-01-28 04:37 AM
Does the measured temperature change approximately by an adequate value when chip is heated (e.g. by touching)?
Did you try repeated measurements?
Do you observe the prescribed time after switching temp sensor on, before taking first measurement?
Is voltage on VREFINT+ pin (or VDDA if this is a small package) rock stable?
> For reference, CAL1 is 950 and CAL2 is 1264, VREFINTCAL is 1507 for my current MCU. The measured raw ADC value for the temperature is 1062.
And raw VREFINT measurement readout is how much?
JW
PS. Generic "temperature readout incorrect" checklist here.
2025-01-28 05:21 AM
Thanks for the detailed return questions.
The temperature rises by around 0.7 °C when I touch it, while it should be a lot more (I think), even after a prolonged time. I repeated the measurements several times also with a different board, with no changes.
With the hint about the warm up time I added a 1 ms Delay before HAL_ADC_Start and HAL_ADC_PollForConversion, but both did not change anything.
The voltage reference is stable, as far as I know. It is supposed to deliver 3 V, but as the hardware is later supplied by a battery, a bit lower like I measured (2.88 V) is also fine.
The measured raw VREFINT value is around 1725.
Also thanks for the checklist, it showed me that a bit has to be supposedly set in ADC_CCR before measuring these channels, but even after adding that manually it did not change anything. It might be that the HAL does that on its own.
What baffles me is that the voltage measurement returns the correct expected value but the temperature measurement does not, even though they use the same code.
2025-01-28 06:24 AM - edited 2025-01-28 06:29 AM
Calculating temperature using values you gave above I get 22,9deg.C
t1 | 30 | TEMP1 temp. |
t2 | 130 | TEMP2 temp. |
TEMP | 1062 | temp.sensor measurement |
TEMP1 | 950 | |
TEMP2 | 1264 | |
CAL | 1507 | |
REFINT | 1725 | VREFINT measurement |
t = t1 + (t2 - t1) * (TEMP*CAL/REFINT - TEMP1) / (TEMP2 - TEMP1)
JW
2025-01-28 06:31 AM - edited 2025-01-28 06:32 AM
In a STM32L4 project, I derived code from the formulae in th ref.man, and not using any library code,. as follows:
// raw 12-bit ADC data
printf("temp_raw_12bit: %4d; vref_raw_12bit: %4d; ", temp_raw_12bit, vref_raw_12bit);
// Calculating the actual VDDA
// factory calibration value, 12-bit raw ADC data
const int vref_cal_raw_12bit = *VREFINT_CAL_ADDR; // stored in engineering bytes area
// factory calibration was done at a fixed vdda of VREFINT_CAL_VREF mV
int vdda_mV = (vref_cal_raw_12bit * VREFINT_CAL_VREF) / vref_raw_12bit; // actual vdda in mV
// Calculating the temperature
int temp_mV = vdda_mV * temp_raw_12bit / 4095; // ADC measurement 12-bit raw --> mV
// factory calibration values, 12-bit raw ADC data.
// measured at two points:
// * 30°C (TEMPSENSOR_CAL1_TEMP)
// * 130°C (TEMPSENSOR_CAL2_TEMP)
const int ts_cal1_raw_12bit = *TEMPSENSOR_CAL1_ADDR; // measured at TEMPSENSOR_CAL1_TEMP °C
const int ts_cal2_raw_12bit = *TEMPSENSOR_CAL2_ADDR; // measured at TEMPSENSOR_CAL2_TEMP °C
// factory calibration was done at a fixed vdda of VREFINT_CAL_VREF mV
// convert the calibration values from 12-bit raw to mV:
int ts_cal1_mV = ts_cal1_raw_12bit * VREFINT_CAL_VREF / 4095;
int ts_cal2_mV = ts_cal2_raw_12bit * VREFINT_CAL_VREF / 4095;
// linear interpolation
int temp_degC = TEMPSENSOR_CAL1_TEMP + (TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP ) * (temp_mV - ts_cal1_mV) / (ts_cal2_mV - ts_cal1_mV);
// converted results
printf("vdda_mV: %4d; temp_degC: %4d\n", vdda_mV, temp_degC);
you may use this to get a second opinion.
hth
KnarfB
2025-01-29 12:38 AM
Your code works correctly and shows me the correct answer. That also led me to the solution - in the example there is this 8-bit shift that is noted to be necessary, but that delivers the wrong value. I don't know why this is in the example code, and I don't know why I kept it. Removing this brings me to the correct temperature.
Thanks for the help!