cancel
Showing results for 
Search instead for 
Did you mean: 

G030 temperature sensor results (very) inaccurate

ZJing
Associate III

Hello all,

I am trying to read temperature with the G030. At the moment there is quite a large difference between actual temperature and temperature read by the MCU (true temperature around 23.5 degC while MCU reads 32 degC)

ADC_Init code:

static void ADC_Init(void)
{
	LL_ADC_InitTypeDef ADC_InitStruct = {0};
 
	LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_SYSCLK);
	LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
 
	LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);
	LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
	LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
	LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&ADC1->DR);
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&reg_buf);
	LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, BL);
 
	ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
	ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
	ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
	LL_ADC_Init(ADC1, &ADC_InitStruct);
	LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE);
 
	volatile uint32_t wait_loop_index = 0;
 
	/* Poll for ADC channel configuration ready */
	while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
	{
		wait_loop_index++;
		if (wait_loop_index >= GENERAL_TIMEOUT)
		{
			Error_Handler();
		}
	}
 
	/* Clear flag ADC channel configuration ready */
	LL_ADC_ClearFlag_CCRDY(ADC1);
 
	LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV8);
	LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_GRP_REGULAR_CONTINUED);
	LL_ADC_ConfigOverSamplingRatioShift(ADC1, LL_ADC_OVS_RATIO_256, LL_ADC_OVS_SHIFT_RIGHT_4);
	LL_ADC_SetOverSamplingDiscont(ADC1, LL_ADC_OVS_REG_CONT);
	LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
	LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
	LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_160CYCLES_5);
	LL_ADC_DisableIT_EOC(ADC1);
	LL_ADC_DisableIT_EOS(ADC1);
 
	/* Enable ADC internal voltage regulator */
	LL_ADC_EnableInternalRegulator(ADC1);
 
	/* Enable VREFINT and TEMPSENSOR */
	SET_BIT(ADC->CCR, LL_ADC_PATH_INTERNAL_VREFINT);
	SET_BIT(ADC->CCR, LL_ADC_PATH_INTERNAL_TEMPSENSOR);
 
	/* Delay for ADC internal voltage regulator stabilization. */
	/* Compute number of CPU cycles to wait for, from delay in us. */
	/* Note: Variable divided by 2 to compensate partially */
	/* CPU processing cycles (depends on compilation optimization). */
	/* Note: If system core clock frequency is below 200kHz, wait time */
	/* is only a few CPU processing cycles. */
	wait_loop_index = 0;
	wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
	while(wait_loop_index != 0)
	{
		wait_loop_index--;
	}
 
	ADC_SetMode(ADC_RUN_REG_InitStruct, ADIN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1);
}

Main clock is 64Mhz, and there is /8 divider on ADC clock, and with 160.5 cycle sampling period, sampling time should be ~20us (which meets sampling time requirement of 5us) unless my math is wrong (however when there is no divider on ADC clock it gives the same result, and sampling time is only ~2.5us)

Temperature sensor reading code:

static void Get_Temp_Vref(void)
{
 
	int32_t result = 0;
 
	LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);
 
	/* Configure ADC for VREFINT sampling*/
	ADC_SetMode(ADC_CAL_REG_InitStruct, LL_ADC_CHANNEL_VREFINT, LL_ADC_SAMPLINGTIME_COMMON_1);
 
	LL_ADC_EnableIT_EOS(ADC1);
	LL_ADC_ClearFlag_EOS(ADC1);
 
	ADC_Start();
 
	while(!LL_ADC_IsActiveFlag_EOS(ADC1));
	LL_ADC_ClearFlag_EOS(ADC1);
	ADC_Stop();
	result = ADC1->DR;
 
	Vref_Factor = 3.0 *(*VREFINT_CAL_ADDR << 4) / result / 65535;			//Vref_Factor * ADC result = actual voltage at ADC input
 
	/* Configure ADC for TEMPSENSOR sampling*/
	ADC_SetMode(ADC_CAL_REG_InitStruct, LL_ADC_CHANNEL_TEMPSENSOR, LL_ADC_SAMPLINGTIME_COMMON_2);
	LL_ADC_EnableIT_EOS(ADC1);
	LL_ADC_ClearFlag_EOS(ADC1);
 
	ADC_Start();
 
	while(!LL_ADC_IsActiveFlag_EOS(ADC1));
	LL_ADC_ClearFlag_EOS(ADC1);
	ADC_Stop();
	result = ADC1->DR;
 
	LL_ADC_DisableIT_EOS(ADC1);
	Temperature = (Vref_Factor * result - (3.0 * *TEMPSENSOR_CAL1_ADDR / 4096.0)) / (0.0025) + 30;
 
}

I read VREFINT first to convert result to absolute voltage, then use that to calculate temperature.

What did I do wrong? Please help! Thanks in advance!

1 ACCEPTED SOLUTION

Accepted Solutions

It may well be that 32°C is the correct readout. After all, the temperature sensor is not measuring the ambient temperature, but temperature of the silicon die.

Btw. what is the actual ADC readout for VREFINT and temperature sensor? And what are the calibration values stored in system memory?

JW

View solution in original post

9 REPLIES 9

Did you perform ADC calibration as outlined in Calibration (ADCAL) subchapter of ADC chapter in RM?

JW

ZJing
Associate III

Hello JW,

Yes, and accuracy is good in other ADC channels.

Please see ADCAL code:

static void ADC_Calibrate()
{
	LL_ADC_Disable(ADC1);
	ADC_Stop();
	LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);
 
	volatile uint32_t wait_loop_index = 0;
	uint32_t calibration_factor_accumulated = 0;
	uint32_t calibration_index;
 
	for (calibration_index = 0UL; calibration_index < 16UL; calibration_index++)
	{
		/* Start ADC calibration */
		LL_ADC_StartCalibration(ADC1);
 
		/* Wait for calibration completion */
		while (LL_ADC_IsCalibrationOnGoing(ADC1))
		{
			wait_loop_index++;
			if (wait_loop_index >= GENERAL_TIMEOUT)
			{
				Error_Handler();
			}
		}
 
		calibration_factor_accumulated += LL_ADC_GetCalibrationFactor(ADC1);
	}
 
	/* Compute average */
	calibration_factor_accumulated /= calibration_index;
	/* Apply calibration factor */
	LL_ADC_Enable(ADC1);
	LL_ADC_SetCalibrationFactor(ADC1, calibration_factor_accumulated);
 
}

KnarfB
Principal III

in addition, you may also check your calcs against the existing helper macros:

uint32_t vref_mV = __LL_ADC_CALC_VREFANALOG_VOLTAGE(vref_raw_adc_data, ADC_RESOLUTION_12B);
 uint32_t temp_C  = __LL_ADC_CALC_TEMPERATURE(vref_mV, temp_raw_adc_data, ADC_RESOLUTION_12B);

hth

KnarfB

It may well be that 32°C is the correct readout. After all, the temperature sensor is not measuring the ambient temperature, but temperature of the silicon die.

Btw. what is the actual ADC readout for VREFINT and temperature sensor? And what are the calibration values stored in system memory?

JW

Hello,

VREFINT is 24067 (VDDA = 3.3, 16bit oversampled) and TEMPSENSOR is 14878 (same conditions), calibration factor is 0x3a (58).

Do these values look right? I think you're right that the actually silicon temperature being higher is the reason, unless one or more of these values are off.

Hi,

Can't really use them as I am using 16bit resolution, so I have to write my own

Yes, these values look OK.

JW

Thanks!

Antoine Odonne
ST Employee

Hello Zjing,

You mentioned a VDDA voltage at 3.3V in a comment, but in temperature calculation in your first post I have the impression the formula is based on a 3V supply (a 3.0 factor remains there)

Best regards,

Antoine