cancel
Showing results for 
Search instead for 
Did you mean: 

offset on the ADC

Ppeyr.1
Associate II

I want to read the Vdd (link to VDDA) of my board with the ADC in a STM32L072. I try read it through a divider bridge or through VrefInt, and both give me a value far below the voltmeter value.

here is my config:

	hadc.Instance = ADC1;
    hadc.Init.OversamplingMode = DISABLE;
    hadc.Init.Oversample.Ratio = ADC_OVERSAMPLING_RATIO_8;
    hadc.Init.Oversample.RightBitShift = ADC_RIGHTBITSHIFT_NONE;
    hadc.Init.Oversample.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
    hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
    hadc.Init.Resolution = ADC_RESOLUTION_12B;
    hadc.Init.SamplingTime = ADC_SAMPLETIME_160CYCLES_5;
    hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
    hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc.Init.ContinuousConvMode = DISABLE;
    hadc.Init.DiscontinuousConvMode = DISABLE;
    hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc.Init.DMAContinuousRequests = DISABLE;
    hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
    hadc.Init.LowPowerAutoWait = DISABLE;
    hadc.Init.LowPowerFrequencyMode = ENABLE;
    hadc.Init.LowPowerAutoPowerOff = ENABLE;
    HAL_ADC_Init(&hadc);
	
    hadc.Instance->CFGR1 = hadc.Instance->CFGR1 | ADC_CFGR1_WAIT;
    /** Configure for the selected ADC regular channel to be converted. */
    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
    HAL_ADC_ConfigChannel(&hadc, &sConfig);
 
    /** Configure Vrefint  */
    sConfig.Channel = ADC_CHANNEL_VREFINT;
    HAL_ADC_ConfigChannel(&hadc, &sConfig);

And to read, I just do twice (one for channel, one for Vref):

HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 1000);
HAL_ADC_GetValue(&hadc);

the results are:

channel1_count = 1047 => 1047 * Vdda(3.3V) / 4096 = 0.84V (multiply by the divider bridge = 3) = 2.53V

with VrefInt, I have:

VrefInt_count = 1575 

VrefInt in register = 1674

1575 / 1674 * 3 = 2.82V

In both cases, I have too small voltage, I measured on a voltmeter, I have ~1.1V on the divider bridge, and 3.3V on Vdda (link to Vdd through a self).

Where is my mistake ? Is there something to check ?

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

> 3M6

> 1M8

Your resistors are much too high. It's dragging down that net during the sample time. Increasing C27 will mitigate the issue. You would also need to ensure the cap is fully charged. Or, since the drop should be about the same percentage of the value each time, you could add a calibration factor to estimate the real value. You also have charge injection happening which can be significant when your impedance is so high.

0693W00000KZxSdQAL.png 

ADC calibration is per-ADC, not per channel, and because VREFINT is measured correctly, it suggests your ADC is performing fine.

Multimeters use a longer sampling time and have a much higher impedance when voltage testing compared to the ADC.

https://www.st.com/resource/en/application_note/cd00211314-how-to-get-the-best-adc-accuracy-in-stm32-microcontrollers-stmicroelectronics.pdf

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

9 REPLIES 9
TDK
Guru

Did you calibrate the ADC? You need to calibrate it before using, per the RM.

> channel1_count = 1047

> 2.53V

Yep. If calibration doesn't fix it, what resistor values are on your bridge? Could be too high impedance.

> VrefInt_count = 1575 

1575 * 3.3 / 4095 = 1.27 V

Even without calibration, seems reasonable to me, if a bit high.

The factory value of 1575 indicates the voltage is

1674 * 3.0 / 4095 = 1.23 V

Using the calibrated value here, VBAT would be calculated as:

VBAT = 3.0 * 1674 / 1575 = 3.19 V

(not the reverse)

If you feel a post has answered your question, please click "Accept as Solution".

Btw, scaling to 4095 is wrong because it distorts the LSB unit size. ADC has 4096 possible values not 4095 and therefore, according to AN2834 section "3 ADC errors", the LSB size is:

1 LSB = VREF+ / 4096

MasterT
Senior III

It's not clear what values of resistors, likely you need small capacitor 0.1uF in between input of the adc and ground, that may help to recharge internal S/H much faster and accurately.

Ppeyr.1
Associate II

Thanks for you answers.

About the divider bridge, a picture is more efficient than words (analog_in is linked to PA1):0693W00000KZseeQAD.pngAbout the calibration, do I need a calibration per channel ? VrefInt and channel1 have not the same offset apparently.

Ppeyr.1
Associate II

I tried to calibrate the ADC, but it's worse.

I add this code right after the init:

    if (HAL_OK == HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED))
    {
        uint32_t calibFactor = HAL_ADCEx_Calibration_GetValue(&hadc, ADC_SINGLE_ENDED);
        HAL_ADCEx_Calibration_SetValue(&hadc, ADC_SINGLE_ENDED, calibFactor);
    }

I checked, HAL_ADCEx_Calibration_Start returns HAL_OK.

But without touching the power supply, I have:

with the calibration channel1_count = 1007, VrefInt_count = 1515

without the calibration channel1_count = 1079, VrefInt_count = 1575

I measure less voltage than before.

TDK
Guru

1515 * 3.3 / 4096 = 1.22V

Seems better to me, not worse. Why do you think it's worse? What do you think it should be?

If you feel a post has answered your question, please click "Accept as Solution".
Ppeyr.1
Associate II

VreftInt measure was roughly OK before the calibration, I'm talking about channel1. I have ~1.2V measured on the multimeter.

And I have:

1007 * 3.3 / 4096 = 0.81V with a calibration

1079 * 3.3 / 4096 = 087V without a calibration

TDK
Guru

> 3M6

> 1M8

Your resistors are much too high. It's dragging down that net during the sample time. Increasing C27 will mitigate the issue. You would also need to ensure the cap is fully charged. Or, since the drop should be about the same percentage of the value each time, you could add a calibration factor to estimate the real value. You also have charge injection happening which can be significant when your impedance is so high.

0693W00000KZxSdQAL.png 

ADC calibration is per-ADC, not per channel, and because VREFINT is measured correctly, it suggests your ADC is performing fine.

Multimeters use a longer sampling time and have a much higher impedance when voltage testing compared to the ADC.

https://www.st.com/resource/en/application_note/cd00211314-how-to-get-the-best-adc-accuracy-in-stm32-microcontrollers-stmicroelectronics.pdf

If you feel a post has answered your question, please click "Accept as Solution".
Ppeyr.1
Associate II

Thanks for you answers, this helps a lot. I will check the impedance, and find a solution. I will come back if it doesn't work.