2022-02-18 08:36 AM
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 ?
Solved! Go to Solution.
2022-02-21 07:24 AM
> 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.
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.
2022-02-18 09:30 AM
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)
2022-02-18 12:31 PM
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
2022-02-18 07:16 PM
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.
2022-02-21 12:27 AM
Thanks for you answers.
About the divider bridge, a picture is more efficient than words (analog_in is linked to PA1):About the calibration, do I need a calibration per channel ? VrefInt and channel1 have not the same offset apparently.
2022-02-21 01:09 AM
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.
2022-02-21 06:36 AM
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?
2022-02-21 06:48 AM
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
2022-02-21 07:24 AM
> 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.
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.
2022-02-21 08:29 AM
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.