K20 and K10 SAR ADC Calibration routine

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

K20 and K10 SAR ADC Calibration routine

Jump to solution
1,506 Views
Capps314
Contributor I

I am running the ADC calibration routine on a K20 and a K10 MCU. When developing the code for the calibration I originally used the COCO bit, as documented in both MCU's reference manuals and in AN3939 and AN4168 for similar ADCs, to determine when the calibration has ended before adding up the registers and putting the results in the offset and gain registers; the COCO bit never became set. I waited over an hour on two occasions.I then found that the CAL bit is cleared when the calibration is complete, in the reference manual's description of the registers, so I wrote my code to look for that condition instead of COCO becoming set and this worked on both a K20 and a K10.

Well a couple months later I learned that on my production units I have an inaccuracy in my ADC results and I tracked it down to be that the ADC calibration is failing. Further investigation showed that the CAL bit was being cleared before calibration had resolved and when setting the gain register the CALF would be set failing the calibration, due to an ADC register being written too. I then re-implemented the COCO bit as the determining factor of when the ADC calibration finished an now that is currently working in my production software (which is very similar to the code where only the CAL bit worked). I am sure that all code that matters is the same between testing code and production code, and I have tried both versions on several MCUs and they behave inconsistently. So my question becomes which method is correct? I need this to work across all units and I have conflicting results.

0 Kudos
1 Solution
671 Views
DavidS
NXP Employee
NXP Employee

Hi Capps314,

The Kinetis baremetal example code has implementation of the ADC16 calibration code.

Goto: K60_100: Kinetis K60 Ethernet Crypto 100 MHz MCUs

Look for following:

KINETIS512_V2_SC : Kinetis 100MHz Rev 2 Example Projects.

Size (K): 705 Format: zip Rev #: 1.0 Modified: 5/23/2013

The KINETIS512_SC contains:

/******************************************************************************
Function 1. Name AUTO CAL ROUTINE  

Parameters  ADC module pointer points to adc0 or adc1 register map
                         base address.
Returns   Zero indicates success.
Notes           Calibrates the ADC16. Required to meet specifications
                        after reset and before a conversion is initiated.
******************************************************************************/
uint8 ADC_Cal(ADC_MemMapPtr adcmap)
{

  unsigned short cal_var;
 
  ADC_SC2_REG(adcmap) &=  ~ADC_SC2_ADTRG_MASK ; // Enable Software Conversion Trigger for Calibration Process    - ADC0_SC2 = ADC0_SC2 | ADC_SC2_ADTRGW(0);  
  ADC_SC3_REG(adcmap) &= ( ~ADC_SC3_ADCO_MASK & ~ADC_SC3_AVGS_MASK ); // set single conversion, clear avgs bitfield for next writing
  ADC_SC3_REG(adcmap) |= ( ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(AVGS_32) );  // Turn averaging ON and set at max value ( 32 )
 
 
  ADC_SC3_REG(adcmap) |= ADC_SC3_CAL_MASK ;      // Start CAL
  while ( (ADC_SC1_REG(adcmap,A) & ADC_SC1_COCO_MASK ) == COCO_NOT ); // Wait calibration end
  
  if ((ADC_SC3_REG(adcmap)& ADC_SC3_CALF_MASK) == CALF_FAIL )
  { 
   return(1);    // Check for Calibration fail error and return
  }

For comparison MQX4.02 ADC Calibration has:

static _mqx_int adc_calibrate(_mqx_int adc_index)

   uint_16 cal_var;

   ADC_MemMapPtr adc_ptr = _bsp_get_adc_base_address(adc_index);

   struct {

   uint_8  SC2;

   uint_8  SC3;

    } saved_regs;

 

   if (adc_ptr == NULL)

        return ADC_ERROR_BAD_PARAM; /* no such ADC peripheral exists */

  saved_regs.SC2 = adc_ptr->SC2;

  saved_regs.SC3 = adc_ptr->SC3;

 

   /* Enable Software Conversion Trigger for Calibration Process */

   adc_ptr->SC2 &= ~ADC_SC2_ADTRG_MASK;

 

   /* Initiate calibration */

   adc_ptr->SC3 |= ADC_SC3_CAL;

 

   /* Wait for conversion to complete */

   while (adc_ptr->SC2 & ADC_SC2_ADACT_MASK){}

 

   /* Check if calibration failed */

  if (adc_ptr->SC3 & ADC_SC3_CALF_MASK) {

           /* Clear calibration failed flag */

           adc_ptr->SC3 |= ADC_SC3_CALF_MASK;

            /* calibration failed */

          return -1;

     }

...


Regards,

David

View solution in original post

0 Kudos
3 Replies
672 Views
DavidS
NXP Employee
NXP Employee

Hi Capps314,

The Kinetis baremetal example code has implementation of the ADC16 calibration code.

Goto: K60_100: Kinetis K60 Ethernet Crypto 100 MHz MCUs

Look for following:

KINETIS512_V2_SC : Kinetis 100MHz Rev 2 Example Projects.

Size (K): 705 Format: zip Rev #: 1.0 Modified: 5/23/2013

The KINETIS512_SC contains:

/******************************************************************************
Function 1. Name AUTO CAL ROUTINE  

Parameters  ADC module pointer points to adc0 or adc1 register map
                         base address.
Returns   Zero indicates success.
Notes           Calibrates the ADC16. Required to meet specifications
                        after reset and before a conversion is initiated.
******************************************************************************/
uint8 ADC_Cal(ADC_MemMapPtr adcmap)
{

  unsigned short cal_var;
 
  ADC_SC2_REG(adcmap) &=  ~ADC_SC2_ADTRG_MASK ; // Enable Software Conversion Trigger for Calibration Process    - ADC0_SC2 = ADC0_SC2 | ADC_SC2_ADTRGW(0);  
  ADC_SC3_REG(adcmap) &= ( ~ADC_SC3_ADCO_MASK & ~ADC_SC3_AVGS_MASK ); // set single conversion, clear avgs bitfield for next writing
  ADC_SC3_REG(adcmap) |= ( ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(AVGS_32) );  // Turn averaging ON and set at max value ( 32 )
 
 
  ADC_SC3_REG(adcmap) |= ADC_SC3_CAL_MASK ;      // Start CAL
  while ( (ADC_SC1_REG(adcmap,A) & ADC_SC1_COCO_MASK ) == COCO_NOT ); // Wait calibration end
  
  if ((ADC_SC3_REG(adcmap)& ADC_SC3_CALF_MASK) == CALF_FAIL )
  { 
   return(1);    // Check for Calibration fail error and return
  }

For comparison MQX4.02 ADC Calibration has:

static _mqx_int adc_calibrate(_mqx_int adc_index)

   uint_16 cal_var;

   ADC_MemMapPtr adc_ptr = _bsp_get_adc_base_address(adc_index);

   struct {

   uint_8  SC2;

   uint_8  SC3;

    } saved_regs;

 

   if (adc_ptr == NULL)

        return ADC_ERROR_BAD_PARAM; /* no such ADC peripheral exists */

  saved_regs.SC2 = adc_ptr->SC2;

  saved_regs.SC3 = adc_ptr->SC3;

 

   /* Enable Software Conversion Trigger for Calibration Process */

   adc_ptr->SC2 &= ~ADC_SC2_ADTRG_MASK;

 

   /* Initiate calibration */

   adc_ptr->SC3 |= ADC_SC3_CAL;

 

   /* Wait for conversion to complete */

   while (adc_ptr->SC2 & ADC_SC2_ADACT_MASK){}

 

   /* Check if calibration failed */

  if (adc_ptr->SC3 & ADC_SC3_CALF_MASK) {

           /* Clear calibration failed flag */

           adc_ptr->SC3 |= ADC_SC3_CALF_MASK;

            /* calibration failed */

          return -1;

     }

...


Regards,

David

0 Kudos
671 Views
Capps314
Contributor I

Thank you for your quick response, I will now chase down a third avenue; the ADACT bit in ADCx_SC2.

0 Kudos
671 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Capps314,

I'm looking forward the result of your third avenue and hope it works.

If you have any further questions, please feel free to contact with me.

Best regards,

Ping