AnsweredAssumed Answered

ADC calibration results vary ?!

Question asked by Slavisa Zlatanovic on May 7, 2014

Hello all,

 

I'm using Freescale KL3x MCU.  Embedded 16-bit A/D converter is capable of performing its own calibration to eliminate offset and linearity problems.

However, I've experienced some problems with this feature.

 

It seems that two successive A/D calibrations result in slightly different calibration data. In my application, high resolution measurement is required, so I'm running

A/D converter in 16-bit mode. First thing after reset is to init A/D converter and perform A/D calibration. Afterward, measurement is done in an endless loop and results are being sent to LCD.

I have noticed that if I reset MCU several times in a row least significant digit on the LCD is changing ± 2 although the input value being measured is fixed (comes from very precise source).

This implies that ADC readings vary. Therefore, I've suspected on the A/D calibration procedure.

 

To investigate this further, I have created simple program that does only A/D convertor init and calibration ... then waits 1 second and does everything again in an infinitive loop.

Calibration results are sent to PC via UART. I've logged this data into txt file (see attachment).

Inside this file, you'll see that every time data is slightly different although the MCU environment (in example: temperature) didn't change.

 

 

Here are calibration results from two successive calibrations:

 

0 --> ADC0_OFS

47 --> ADC0_CLP0

92 --> ADC0_CLP1

187 --> ADC0_CLP2

372 --> ADC0_CLP3

740 --> ADC0_CLP4

48 --> ADC0_CLPS

33511 --> ADC0_PG

49 --> ADC0_CLM0

97 --> ADC0_CLM1

192 --> ADC0_CLM2

382 --> ADC0_CLM3

755 --> ADC0_CLM4

48 --> ADC0_CLMS

33529 --> ADC0_MG

 

 

65535 --> ADC0_OFS

49 --> ADC0_CLP0

96 --> ADC0_CLP1

193 --> ADC0_CLP2

385 --> ADC0_CLP3

767 --> ADC0_CLP4

49 --> ADC0_CLPS

33537 --> ADC0_PG

50 --> ADC0_CLM0

100 --> ADC0_CLM1

198 --> ADC0_CLM2

392 --> ADC0_CLM3

776 --> ADC0_CLM4

49 --> ADC0_CLMS

33550 --> ADC0_MG

 

 

I have read that calibration results can be affected by the clock source, frequency, power and conversion speed settings, voltage reference, hardware average function, the

sample time and to a much lesser extent, environment. I've followed all the instructions from the datasheet. Here is the relevant code snippet:

 

uint8_t Init_ADC(void)

{

unsigned short nCal;

uint8_t autocal_result;

 

  // Turn on the ADC0 clock

  SIM_SCGC6 |= SIM_SCGC6_ADC0_MASK;

 

  // Select TPM2 as ADC 0 trigger source

  SIM_SOPT7 = SIM_SOPT7_ADC0ALTTRGEN_MASK | SIM_SOPT7_ADC0TRGSEL(10);

 

  //Normal power configuration

  //Input Clock is (Bus Clock)/2

  //The divide ratio is 1 and the clock rate is input clock

  //ADC clock = 24M/2/4 = 3MHz

  //Long sample time

  //16-bit conversion

  ADC0_CFG1 = 0x5D;

 

  //ADxxa channels are selected

  //Asynchronous clock output disabled; Asynchronous clock is enabled only if selected by ADICLK

  //and a conversion is active.

  //High-speed conversion sequence selected with 2 additional ADCK cycles to total conversion time.

  //Default longest sample time; 20 extra ADCK cycles; 24 ADCK cycles total.

  ADC0_CFG2 = 0x4;

 

  //Hardware trigger selected

  //Compare function disabled

  //DMA disabled

  //Default voltage reference pin pair, that is, external pins VREFH and VREFL

  ADC0_SC2 = ADC_SC2_ADTRG_MASK;

 

  //Hardware average function enabled

  //32 samples averaged

  ADC0_SC3 = 0x7;

 

  //Conversion complete interrupt enabled

  //Single-ended conversions

  //AD4 is selected as input

  ADC0_SC1A = ADC_SC1_AIEN_MASK | 0x4;

 

 

/* ADC calibration */

 

  //The input channel, conversion mode continuous function, compare function, resolution

  //mode, and differential/single-ended mode are all ignored during the calibration function.

  //In 16-bit single-ended mode, there is no field in the OFS

  //corresponding to the least significant result D[0], so odd values, such as -1 or +1, cannot

  //be subtracted from the result.

 

  ADC0_SC1A &= ~ADC_SC1_AIEN_MASK;

 

  ADC0_SC2 = 0x1; //Enable Software Conversion Trigger for Calibration Process; Alternate reference pair (Vrefh = VDDA).

 

  ADC0_SC3 |= ADC_SC3_CAL_MASK; //Start calibration

 

  while(!(ADC0_SC1A & ADC_SC1_COCO_MASK));

 

  if (ADC0_SC3 & ADC_SC3_CALF_MASK)

  {

      // Calibration failed

      autocal_result = 0;

  }else{

          // Calculate plus-side calibration

            nCal = 0x00;

            nCal =  ADC0_CLP0;

            nCal += ADC0_CLP1;

            nCal += ADC0_CLP2;

            nCal += ADC0_CLP3;

            nCal += ADC0_CLP4;

            nCal += ADC0_CLPS;

 

            nCal = nCal >> 1;

            nCal |= 0x8000;

            ADC0_PG = ADC_PG_PG(nCal);

 

            // Calculate minus-side calibration

            nCal = 0x00;

            nCal =  ADC0_CLM0;

            nCal += ADC0_CLM1;

            nCal += ADC0_CLM2;

            nCal += ADC0_CLM3;

            nCal += ADC0_CLM4;

            nCal += ADC0_CLMS;

 

            nCal    = nCal >> 1;

            nCal    |= 0x8000;

            ADC0_MG = ADC_MG_MG(nCal);

   

            autocal_result = 1;

    }

 

  ADC0_SC3 &= ~ADC_SC3_CAL_MASK;

 

//Restore original settings

 

  ADC0_SC1A = ADC_SC1_AIEN_MASK | 0x4;

  ADC0_SC2 = ADC_SC2_ADTRG_MASK;

 

 

/*End - ADC calibration */

 

  //Configure interrupts

  //Set Priority 2

  // NVIC_IPR3: PRI_1=0x80

  NVIC_IPR3 = (uint32_t)((NVIC_IPR3 & (uint32_t)~(uint32_t)(NVIC_IP_PRI_15(0x3F))) | (uint32_t)(NVIC_IP_PRI_15(0x80)));

  NVIC_ICPR |= 0x00008000; //Clear any pending interrupts

  NVIC_ISER |= NVIC_ISER_SETENA(0x00008000); //Enable ADC0 interrupts

 

  return autocal_result;

}

     

 

I would appreciate your opinion on this matter. Have I overseen something or have I reached the limits of A/D converter?

Thanks in advance!

Original Attachment has been moved to: adc_calibration.txt.zip

Outcomes