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.
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
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
Thank you for your quick response, I will now chase down a third avenue; the ADACT bit in ADCx_SC2.
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