2026-04-13 8:18 AM
I have followed the datasheets and tried to calculate my reference voltage. I am supplying steady 3V on Vref+ pin but I wanted to confirm this because I have some troubles with the ADC precision.
So I read the internal voltage both on my card and on a nucelo-C092rc. This is my results:
My board:
Vref+ = 3.0 * 1659 / 1805 = 2.755
Nucelo board:
Vref+ = 3.0 * 1665 / 1641 = 3.043
However I am quite certain that the nucelo boards are using 3.3 V as reference and my board are using 3 V as reference.
So if I swap the 3.0 V from the datasheet into 3.3 V I get the following:
My board:
Vref+ = 3.3 * 1659 / 1805 = 3.033
Nucelo board:
Vref+ = 3.3 * 1665 / 1641 = 3.348
Which match more my expectations, can someone confirm or deny this typo?
I add my adc init function:
u16 uiDelay = 0;
ADC_CFGR2.CKMODE = 0x0; // Clock mode set to ADCCLK
ADC_CCR.PRESC = 0x2; // ADC prescaler set to 48MHz / 4 = 12MHz
ADC_CFGR1.RES = 0x0; // Set 12 bit resolution
ADC_SMPR = 0x07; // Sampling time set to 1,5 ADC clock cycles, ADC_ACTIVE_CHANNELS*Sampling time should be less than the ISR period to insure we have new
// measurements
ADC_CCR.VREFEN = 1; // Enable VREFINT
ADC_CFGR1.EXTEN = 0x1; // Trigger ADC rising edge
ADC_CFGR1.EXTSEL = 0x0; // Trigger conversion on TIM1_TRGO
ADC_CFGR1.SCANDIR = 0; // 0 = scan from CHSEL0 to max
ADC_CFGR1.ALIGN = 0; // Right data align AC results
ADC_CHSELR = ADC_ACTIVE_CHANNELS; // Select channels to be scanned
ADC_CR.ADVREGEN = 1; // Enable the ADC Voltage regulator
for (int i = 0; i < ADC_SETUP_TIME; i++); // After ADC_CR.ADVREGEN is set, the mcu have to wait t_ADCVREG_STUP before calibration, see datasheet
ADC_CR.ADCAL = 1; // Initiate calibration of the ADC
while (ADC_CR.ADCAL); // Loop until the calibration is complete
ADC_CR.ADEN = 1; // Enable the ADC
while (ADC_ISR.ADRDY == 0) // After ADC_CR.ADEN is set it needs the time t_STAB to be ready, see datasheet.
{
uiDelay++;
if (uiDelay > ADC_SETUP_TIME) // If the ADC is not ready after some time, re-enable it.
{
uiDelay = 0;
ADC_CR.ADEN = 1;
}
}
DMA1.DMA_CH1.CPAR = (u32)(&(ADC_DR)); // Set DMA to read from ADC data register
DMA1.DMA_CH1.CMAR = (u32)(gv_vuiADC_RSLT); // Write destination
DMA1.DMA_CH1.CNDTR = ADC_Last; // No of bytes to be processed acc to datasheet??? #no of channels according to example
DMA1.DMA_CH1.CCR.MSIZE = 1; // Memory size is 16 bits
DMA1.DMA_CH1.CCR.PSIZE = 1; // Peripheral size is 16 bits
DMA1.DMA_CH1.CCR.MINC = 1; // Enable memory increment mode
DMA1.DMA_CH1.CCR.CIRC = 1; // Circular adressing
DMA1.DMA_CH1.CCR.DIR = 0; // Read from peripheral
DMAMUX_C0CR.DMAREQ_ID = 0x05; // Set DMA request identification, ADC is 5;
DMAMUX_C0CR.EGE = 0; // Event generation disabled
DMAMUX_C0CR.SE = 0; // Sync Disabled
DMAMUX_C0CR.NBREQ = 0x01; // Number of DMA requests
u16 uiGarbage = ADC_DR; // Read the ADC Data register to clear prior conversion flags
ADC_CFGR1.DMAEN = 1; // Enable DMA
ADC_CFGR1.DMACFG = 1; // Circular mode
DMA1.DMA_CH1.CCR.EN = 1; // Enable DMA
ADC_CR.ADSTART = 1; // Start a conversion
Solved! Go to Solution.
2026-04-16 5:20 AM
Yes correct, I found it. The ADC self calibration didnt apply
2026-04-14 2:44 AM
The Vrefint nominal value is 1.23V, which corresponds to ADC readout of 1679 at Vref+=3.0V, which quite well corresponds to VREFINT_CAL what you've read out from both chips' system memory.
I'd say, the problem is in your program, either in calibration or somewhere in the measurement. You don't use the ST-provided CMSIS-mandated device headers, so even there can be errors.
Try to measure a grounded pin, or a pin at Vref/2.
JW
2026-04-16 5:20 AM
Yes correct, I found it. The ADC self calibration didnt apply
2026-04-16 8:32 AM
Thanks for coming back with the solution.
> ADC self calibration didnt apply
Can you please tell us, why? A quick look at the code provided above appears to show that you did perform the self calibration.
JW