cancel
Showing results for 
Search instead for 
Did you mean: 

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

char_array
Associate III

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;
}

1 ACCEPTED SOLUTION

Accepted Solutions
Bob S
Principal

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

View solution in original post

4 REPLIES 4
Bob S
Principal

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

GLASS
Senior

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...

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.

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.