 
					
				
		
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.
Solved! Go to Solution.
 
					
				
		
 DavidS
		
			DavidS
		
		
		
		
		
		
		
		
	
			
		
		
			
					
		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.
 : 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
 
					
				
		
 DavidS
		
			DavidS
		
		
		
		
		
		
		
		
	
			
		
		
			
					
		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.
 : 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
 
					
				
		
Thank you for your quick response, I will now chase down a third avenue; the ADACT bit in ADCx_SC2.
 
					
				
		
 jeremyzhou
		
			jeremyzhou
		
		
		
		
		
		
		
		
	
			
		
		
			
					
		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
