Calibrating STM32 ADC (VREFINT)

iter picture iter · Oct 10, 2019 · Viewed 7k times · Source

I'm trying to read VDDA on an STM32F042 microcontroller. I'm getting unexpected results with VDD at 3.29V. I must be missing something fundamental.

output:

VREFINT=1917; VREFINT_CAL=1524; VDDA=2623 mV
VREFINT=1885; VREFINT_CAL=1524; VDDA=2668 mV
VREFINT=1913; VREFINT_CAL=1524; VDDA=2628 mV
VREFINT=1917; VREFINT_CAL=1524; VDDA=2623 mV
VREFINT=1917; VREFINT_CAL=1524; VDDA=2623 mV

adc_test.c:

#include <stdio.h>
#include "stm32f0xx.h"

#define VREFINT_CAL_ADDR                0x1FFFF7BA  /* datasheet p. 19 */
#define VREFINT_CAL ((uint16_t*) VREFINT_CAL_ADDR)

extern void initialise_monitor_handles(void);

int main(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;     /* enable ADC peripheral clock */
    RCC->CR2 |= RCC_CR2_HSI14ON;            /* start ADC HSI */
    while (!(RCC->CR2 & RCC_CR2_HSI14RDY)); /* wait for completion */
    /* calibration */
    ADC1->CR |= ADC_CR_ADCAL;               /* start ADc CALibration */
    while (ADC1->CR & ADC_CR_ADCAL);        /* wait for completion */
    ADC1->CR |= ADC_CR_ADEN;                /* ADc ENable */
    while (!(ADC1->ISR & ADC_ISR_ADRDY));   /* wait for completion */
    ADC1->SMPR |= ADC_SMPR1_SMPR_0 |        /* sampling mode: longest */
      ADC_SMPR1_SMPR_1 |
      ADC_SMPR1_SMPR_2;
    /* VDD reference */
    ADC->CCR |= ADC_CCR_VREFEN;             /* VREF Enable */
    ADC1->CHSELR = ADC_CHSELR_CHSEL17;      /* CH17 = VREFINT */

    initialise_monitor_handles();           /* enable semihosting */

    while (1) {
        ADC1->CR |= ADC_CR_ADSTART;             /* start ADC conversion */
        while (!(ADC1->ISR & ADC_ISR_EOC));     /* wait for completion */
        uint32_t vdda = 3300UL * *VREFINT_CAL / ADC1->DR; /* ref. manual p. 252; constant and result in millivolts */
        printf("VREFINT=%lu; VREFINT_CAL=%lu; VDDA=%lu mV\n",
                (unsigned long)ADC1->DR,
                (unsigned long)*VREFINT_CAL,
                (unsigned long)vdda);
    }
}

Screenshot from Datasheet:

enter image description here

Screenshot from Reference Manual

note this refers to .3V, but I believe this to be a typo, as the datasheet above and the longer formula below refer to 3.3V, and .3V is below minimum operating voltage for this part

enter image description here

Answer

Artur Sparwasser picture Artur Sparwasser · Oct 13, 2020

I'm currently developing an ADC driver for STM32L4. During implementation I encounter almost the same problem. In my opinion the first formula enter image description here

is not calculating the VDDA, but VREF+. It's the voltage against which the ADC is evaluating the ADC-IN channels. Further the VREFINT_DATA is not measured VREF+ voltage, but an internal reference voltage which is controller dependent. In my case it defined in controller datasheet: enter image description here

Here's a pic how I am using the posted formulas: enter image description here

Some comments: ln 102: calculating VREF+ not VDDA

ln 105-110: calculate all ranks/configured sequence

ln 108: calculate voltage measured by ADCpin_x

ln 109: multiply by gain to get real value

In my opinion by calculating the VREF+ for each conversion sequence, I'll get better results, because so some ripples on VREF+ are compensated.