cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L011F4 ADC Calibration issues

Red Dwarf
Associate

We have ongoing issues with calibration of the ADC.

Two things stand out, and I have (sort of) fixed them, but I would like to know WHY its needed (and possibly if there is a better way.) 

1) I'm getting inconsistent calibration results. Causing the ADC readings to be of by a significant amount. I currently solve this by looping till I get the same result twice... It works, but its ugly. 

2) I have to disable the ADC after calibration. Otherwise calibration is ignored for the first ADC sample. (Adding a delay don't help here. Need to exility disable the ADC.) 

Configuration is done before calibration. 

See code below (I included code for configuration too):

/**
 * @fn bool ADC_Calibaration(void)
 * @brief Calibrating the ADC (should be done before enable.)
 * @return
 */
bool ADC_Calibaration(void)
{
uint32_t DMA_backUp;
uint32_t old_cal;

if (ADC1->CR & ADC_CR_ADEN) {
return(false);
}

DMA_backUp = ADC1->CFGR1 & (ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG);
ADC1->CFGR1 &= ~(ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG); // disable DMA

do { // should cure inconsistent calibration results (gets wrong about 1 in 30)
old_cal = ADC1->CALFACT;
ADC1->CR |= ADC_CR_ADCAL; // Initiate calibration
while ((ADC1->CR & ADC_CR_ADCAL) != 0) ; // wait for calibration
} while(old_cal != ADC1->CALFACT);

ADC1->CR |= ADC_CR_ADDIS; // first sample after calibration will get wrong result without this. It's not in the doc's.
ADC1->CFGR1 |= DMA_backUp;

return(true);
}

/**
 * @fn void ADC_Config(void)
 * @brief 160.5us sample time, 16x oversampling. Uses DMA for auto transfer to buffer.
 */
void ADC_Config(void)
{
/* Configure ADC1 */
// When selecting an analog ADC clock frequency lower than 3.5 MHz, it is mandatory to first
// enable the Low Frequency Mode by setting bit LFMEN = 1 into the ADC_CCR register
ADC->CCR |= ADC_CCR_LFMEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // Enable the peripheral clock on ADC1
ADC1->CFGR2 |= ADC_CFGR2_CKMODE_0; // PCLK/2 (Synchronous clock mode) (2MHz)
ADC1->CFGR1 |= ADC_CFGR1_AUTOFF; // Stop after scan.
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; // 111: 160.5 ADC clock cycles. (2MHz gives -> (1/2)*2*160.5) = 160.5us)
ADC1->IER = ADC_IER_OVRIE; // Enable interrupt on overrun.
ADC->CCR |= ADC_CCR_VREFEN; // Wake-up the VREFINT

ADC1->CR |= ADC_CR_ADVREGEN;
/* Enable over-sampling with ratio 16 and no shift */
ADC1->CFGR2 |= (ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSR_0); // 16x

/* Configure NVIC for ADC */
NVIC_EnableIRQ(ADC1_COMP_IRQn); // Enable Interrupt on ADC
NVIC_SetPriority(ADC1_COMP_IRQn, 0); // Set priority for ADC

/* Configure DMA1 */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable the peripheral clock on DMA1
ADC1->CFGR1 |= ADC_CFGR1_DMAEN; // Enable DMA transfer on ADC.
DMA1_Channel1->CPAR = (uint32_t) &(ADC1->DR); // Configure the peripheral data register address
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_TEIE; // Interrupt on error, NO Interrupt on complete.
DMA1_Channel1->CCR |= DMA_CCR_EN; // Enable DMA Channel 1

/* Configure NVIC for DMA */
NVIC_EnableIRQ(DMA1_Channel1_IRQn); // Enable Interrupt on DMA Channel 1
NVIC_SetPriority(DMA1_Channel1_IRQn,0); // Set priority for DMA Channel 1

return;
}

 

0 REPLIES 0