2025-03-13 5:25 PM
float read_internal_temperature(void)
{
uint32_t adcValue;
float temperature;
// Configure ADC channel for temperature sensor
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// Start conversion and wait for it to complete
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
// Read ADC value
adcValue = HAL_ADC_GetValue(&hadc1);
// Retrieve calibration values from system memory
uint16_t ts_cal1 = TS_CAL1; // Calibration value at 30°C
uint16_t ts_cal2 = TS_CAL2; // Calibration value at 110°C
// Calculate the slope between TS_CAL1 and TS_CAL2
float slope = (float)(ts_cal2 - ts_cal1) / (110.0f - 30.0f);
// Calculate the temperature
temperature = 30.0f + ((float)(adcValue - ts_cal1) / slope);
// Stop ADC to save power
HAL_ADC_Stop(&hadc1);
return temperature;
}
The adcValue returns very low value although I am using a 12 bit resolutio
2025-03-13 7:37 PM
Do you calibrate the ADC on startup?
Does your VREF+ match what was used for TS_CALx? (in datasheet)
2025-03-14 2:47 AM - edited 2025-03-14 2:51 AM
Isn't TS_CAL1 an address? You need to de-reference it first. Also I would invert the slope calculation so you only need to multiply instead of dividing.
If your VREF voltage is not the same you need to scale your voltage or the CAL values.
2025-03-14 7:41 AM
Thanks all for your help!
I confirm my Vref match the CAL values in my data sheet.
Initial ADC calibration
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; // Enable scan mode for multiple channels
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 2; // Two conversions (Temp sensor and VREFINT)
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.DFSDMConfig = ADC_DFSDM_MODE_DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel for Temperature Sensor (ADC_CHANNEL_TEMPSENSOR)
*/
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel for VREFINT (ADC_CHANNEL_VREFINT)
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_2; // Second conversion
sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
My temperature reading code
float read_internal_temperature(void)
{
uint32_t adc_vref_raw;
uint32_t adc_temp_raw;
// Start the ADC conversion
HAL_ADC_Start(&hadc1);
// Wait for conversion to complete for Temperature channel
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
adc_temp_raw = HAL_ADC_GetValue(&hadc1); // Temperature sensor value
// Wait for conversion to complete for VREFINT channel
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); // Ensure the next channel is ready
adc_vref_raw = HAL_ADC_GetValue(&hadc1); // VREFINT value
// Retrieve calibration values for temperature sensor and VREFINT
uint16_t ts_cal1 = (*TEMPSENSOR_CAL1_ADDR); // Calibration at 30°C
uint16_t ts_cal2 = (*TEMPSENSOR_CAL2_ADDR); // Calibration at 110°C
uint16_t vrefint_cal = (*VREFINT_CAL_ADDR); // VREFINT calibration
// Calculate VDDA (the reference voltage)
float vdda = (3.0 * vrefint_cal) / adc_vref_raw;
// Calculate temperature based on calibration values
float slope = (float)(ts_cal2 - ts_cal1) / (110.0f - 30.0f);
float temperature = 30.0f + ((float)(adc_temp_raw - ts_cal1) / slope);
// Stop the ADC conversion
HAL_ADC_Stop(&hadc1);
// You now have the calibrated temperature and VDDA (reference voltage)
return temperature;
}
Final Output:
adc_temp_raw = 901 -> again too low
adc_vref_raw = 1443
ts_cal1 = 1040
ts_cal2 = 1384
vrefint_cal = 1657
vdda = 3.4V
2025-03-14 8:28 AM - edited 2025-03-14 8:34 AM
You are not calibrating your ADC in MX_ADC1_Init(). A common mistake.
Use HAL_ADCEx_Calibration_Start() or LL_ADC_StartCalibration().
Also wait after calibration before starting: https://community.st.com/t5/stm32-mcus-products/adc-register-adrdy-never-sets-when-i-enable-the-adc-so-it-keeps/m-p/106159#M17007
2025-03-14 8:31 AM
> vdda = 3.4V
TS_CAL values are taken at 3.0 V per the datasheet.
So not the same.
Scale adc_temp_raw by 3.4 / 3.0 which gives 1021.
Put that in the calculation and you'll get a temp just under 30 C which is reasonable.