Skip to main content
char_array
Associate III
September 7, 2022
Solved

How to accurately measure VDDA? I'm of by 5%. All ADC measurements are too low.

  • September 7, 2022
  • 2 replies
  • 2504 views

I supply my STM32G4 with 3.3V.

The 3.3V is more like 3.246 Volt.

The VREF+ is connected externally to VDDA, which is connected to the 3.246V.

My VREFINT_CAL_ADDR is 1662. On the Vref channel of the ADC I measure 1465, so I calculated 3.4034V for VDDA. This is 4.8% too high.

I would expect a more accurate value. I use an Agilent bench multimeter.

My regular multimeter is off by less than 1% from the bench, so it is definitely the ADC that is not accurate.

On another channel I measure a voltage from an opamp.

The measurement is 15% lower than what I measure with my multimeter.

I've initally had the ADC clock set to 32MHZ, but I lowered it to the lowest value possible and it didn't make a difference.

static const float VddaNumerator = float(*VREFINT_CAL_ADDR)*VREFINT_CAL_VREF/1000;
 
[[maybe_unused]] static bool testAdc()
{
 ADC_ChannelConfTypeDef sConfig = {0};
 uint16_t value;
 float voltage;
 
 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;
 
 printf("VREFINT_CAL_ADDR: %d\n", *VREFINT_CAL_ADDR);
 
 sConfig.Channel = LL_ADC_CHANNEL_VREFINT; // internal reference voltage, measured with VDDA/VREF+
 HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 HAL_ADC_Start(&hadc1);
 HAL_ADC_PollForConversion(&hadc1, 100);
 HAL_ADC_Stop(&hadc1);
 value = HAL_ADC_GetValue(&hadc1);
 printf("ADC channel VREFINT: %d\n", value);
 
 
 HAL_ADC_Start(&hadc1);
 HAL_ADC_PollForConversion(&hadc1, 100);
 HAL_ADC_Stop(&hadc1);
 value = HAL_ADC_GetValue(&hadc1);
 printf("ADC channel VREFINT: %d\n", value);
 
 if(value != 0)
 {
 float vdda = VddaNumerator/value;
 printf("VDDA: %0.4f\n", vdda);
 
 
 sConfig.Channel = ADC_CHANNEL_1; // 0-10V channel
 HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 HAL_ADC_Start(&hadc1);
 HAL_ADC_PollForConversion(&hadc1, 100);
 HAL_ADC_Stop(&hadc1);
 value = HAL_ADC_GetValue(&hadc1);
 printf("ADC channel 1: %d\n", value);
 
 voltage = float(value) * ((112.0f/12.0)*(3.3f/(4095))) ;
 printf("ADC channel 1 voltage: %0.3f V\n", voltage);
 
 
 voltage = float(value) * vdda * ((112.0f/12.0)/(4095));
 printf("ADC channel 1 voltage calibrated: %0.3f V\n", voltage);
 }
 
 return true;
}

This topic has been closed for replies.
Best answer by Bob S

Do you calibrate the ADC (i.e. HAL_ADCEx_Calibration_Start()) anywhere?

2 replies

Bob S
Bob SBest answer
Super User
September 7, 2022

Do you calibrate the ADC (i.e. HAL_ADCEx_Calibration_Start()) anywhere?

char_array
Associate III
September 8, 2022

Thank you. For some reason I missed this as this was not mentioned clearly in the datasheet and this function was in a different header file. Now it works with about 0.5% error. More than accurate enough.

GLASS
Visitor II
September 8, 2022

If g4 adc are similar to f3 you need​ to calibrated as stated by Bob S.

I don't already used G4...​

For other family with ST factory calibration, may be you need to change code to manage a var and not a const, because you need to read in chip and not to fix a calibration value at compil time...

char_array
Associate III
September 8, 2022

You are right the calibration value is not a compile time constant. But the keyword const doesn't mean compile time constant, it just means the compiler prevents you from writing to it. How it's stored is up to the compiler. Since dereferencing an address is not compile time constant the compiler will not try to store the const object in flash. I think the value is stored in ram by the RTE initialization before the main starts. Unlike const constexpr requires the data to be compile time const, so compilation will fail if you use constexpr on pointers.