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.
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
#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);
}
}
I'm currently developing an ADC driver for STM32L4. During implementation I encounter almost the same problem. In my opinion the first formula
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:
Here's a pic how I am using the posted formulas:
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.