cancel
Showing results for 
Search instead for 
Did you mean: 

Not the best ADC accuracy of STM32F0 even after calibration

Christian Wächter
Associate III
Posted on June 19, 2018 at 14:13

Hello everyone,

I'm currently trying to get as accurate ADC values as possible from a STM32F072. I'm using the NUCLEO-F072RB and wanted to use the internal stored reference voltage value for calibration. But I'm already off when I use this to calculate my actual VDDA for further ADC measurement. I double checked all my voltages with a calibrated multimeter and oscilloscope directly on the pins and with smalles possible ground loops. There are no spikes/drops in the voltage due to the ADC measurement and the ripple and noise is at around 5 mVpp. I also added 2x 10 nF and 1x 10 uF to C30 (100 nF) on AVDD on the nucleo. I tried all sampling times for the ADC without any change in the results and of course use 12-Bit. I do not want to read the value of the internal Vref-Voltage as it is not accurate (min 1.2V, typ 1.23V, max 1.25V).

So even after taking care of any possible error sources I'm getting a wrong value for my calculated AVDD voltage. I measured this with two multimeters to be 3.310V at 25�C. The calculated value from the STM32 is ~3.296 V, which is 14 mV off. This might be too much for measurements of voltages after a voltage divider, even when considering the 1% tolerance of the resistors. I read about a accuracy of ~1 mV (without voltage divider), so I'm curious how to achieve this when the reference value is not accurate.

This are parts of the code from the used test program:

// ADC self calibration, has to be done before any ADC Start/Enable

while(HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK);

// values for calculation of actually VDDA-supply and reference voltage

volatile uint32_t VREFINT_CAL, VREF_3V3 = 330000;

const uint16_t *p = (uint16_t *)0x1FFFF7BA; // 2 Byte at this address is VRefInt @3.3V/30�C

VREFINT_CAL = *p; // read the value at pointer address

// Start ADC with DMA support

volatile uint16_t adc_value[4] = {0, 0, 0, 0};

HAL_ADC_Start_DMA(&hadc, (uint32_t*)adc_value, 4);

 

while (1)

{

   //toggle LED

   HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);

   HAL_Delay(50);

   // calculate 3.3 V supply voltage with the factory stored VREFINT_CAL value

   // adc_value[2] contains the raw ADC_IN17 value of the internal voltage reference (~1530)

   VREF_3V3 = (330000 * VREFINT_CAL) / adc_value[2];

   // VREFINT_CAL = 1529

   // adc_value[2] = 1530

   // VREF_3V3 = ~329568 (~3.29568 V calculated)

}

#accuracy #nucleo-f072rb #adc #calibration #stm32f0 #internal-voltage-reference
21 REPLIES 21
Posted on June 20, 2018 at 14:12

USB powered Nucleo

and the two pins OSB_DM, DP are at 5V levels ? or 3V ?  can you see on the scope ?

can you remove the USB connector and still see the readings ?

Posted on June 20, 2018 at 15:13

The USB data lines are a kind of a differential signal and do not have a specific level to GND. Scope shows minimum levels of -500 mV and +3.5 V in referece to GND. But this USB signal is not connected to the microcontroller I'm trying to get the ADC working. On the Nucleo boards, there is a STM32F103 that is connected via USB and works as STLink. It than is connected via UART (3.3V) to the STM32F072

Why do you think that the +5V are causing such problems? Can you provide a source for that?

Posted on June 20, 2018 at 15:42

Some threads were discussing 5V tolerant pins pulling internally on the ADC rail, thereby affecting the readings substantially

just a thought.

Glen Larson
Associate II
Posted on June 21, 2018 at 15:52

Set up an application-level calibration that converts the voltage back into whatever engineering unit is actually being measured.

Did you check the data sheet to see what the specified accuracy of the ADC is? Running the numbers shows the measurement is off by 0.4% of reading. I regard that as excellent.

For another implementation angle, I run the ADC of STM32 processors using DMA and very fast. Then do an Infinite Impulse Response filter in binary logic in the interrupt. Super fast and super stable. The read voltage is converted to whatever is needed (Voltage, currrent, temperature, etc...) in the application code along with a final calibration stage that uses different techniques to address end-to-end the non-linearities and inaccuracies of the signal.

It sounds like I'm having a similar problem on my STM32F070CB. I put 0.6V into AN1 CH9 and it reads 679 Counts (which translates to 0.54V). At 0.6V I should be reading 740 counts. VDDA is 3.319V. I have confirmed my voltage in with 2x multimeters and an O-Scope. DId you ever find a solution?

I also have the same issue, exactly the same, down to the 14 mV (from the original poster). It could be related to DMA usage, the DMA automatic conversion sequence might be the issue here. I haven't had time to check yet.

T J
Lead
Not great around zero volts, but within 1-2mV over the range after you calibrate every minute.
You really only need to calibrate again and again if the temperature of the processor is changing.
Glen Larson
Associate II

Check the data sheet for your parts against the specs the system needs. If the design needs the ADC to be accurate to 1 bit, it's not going to make spec. Checking the M4F I'm using, it'll be 3-4 LSB at best and on top of Vref errors and noise.

Four suggestions:

Use a precision supply on Vref

Apply the cal from Vrefin_cal

Calibrate each unit in production

Add a better external ADC

​Figured out the solution to this, you need to calibrate the ADC anytime you restart the MCU.  Use this command if you are using the HAL libraries:  HAL_ADCEx_Calibration_Start(&hadc);

I am doing that :(

I'm using a different library, I've compared the source of the HAL library to what I'm using and its equivilent. The ADC is off before ADCAL is set as it should be.