ADC calibration intermittently freezes

Showing results for 
Search instead for 
Did you mean: 

ADC calibration intermittently freezes

Contributor II

I'm using a modified setup of the ADC from the ch. 11 example of the Kinetis L series peripherals users guide (KLQRUG) to configure a single channel input on the TWR-KL25 platform. I'm running into intermittent unexpected behavior where, as I launch to program through the debug perspective in Codewarrior, it will sometimes freeze during the ADC calibration process. Specifically, it'll freeze a the following while statement in the calibration function (line 11):

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   }   // Calculate plus-side calibration   cal_var = 0x00;     cal_var =  ADC_CLP0_REG(adcmap);   cal_var += ADC_CLP1_REG(adcmap);   cal_var += ADC_CLP2_REG(adcmap);   cal_var += ADC_CLP3_REG(adcmap);   cal_var += ADC_CLP4_REG(adcmap);   cal_var += ADC_CLPS_REG(adcmap);

  cal_var = cal_var/2;   cal_var |= 0x8000; // Set MSB

  ADC_PG_REG(adcmap) = ADC_PG_PG(cal_var);

  // Calculate minus-side calibration   cal_var = 0x00;

  cal_var =  ADC_CLM0_REG(adcmap);   cal_var += ADC_CLM1_REG(adcmap);   cal_var += ADC_CLM2_REG(adcmap);   cal_var += ADC_CLM3_REG(adcmap);   cal_var += ADC_CLM4_REG(adcmap);   cal_var += ADC_CLMS_REG(adcmap);

  cal_var = cal_var/2;

  cal_var |= 0x8000; // Set MSB

  ADC_MG_REG(adcmap) = ADC_MG_MG(cal_var);     ADC_SC3_REG(adcmap) &= ~ADC_SC3_CAL_MASK ; /* Clear CAL bit */

  return(0); }

This doesn't always happen, and when it does, the only fix that seems to work consistently is to do a full off-on power cycle. Are there any known bugs with the calibration step? If it helps, the general initialization process is:

Clocks and system resources

DMA configuration (for sample transfers to memory)

ADC configuration (including calibration step)

LPTMR (acts as HW trigger to ADC)


Labels (1)
0 Kudos
4 Replies

Contributor III

I read this regarding the CAL field of ADCx_SC3:

CAL begins the calibration sequence when set. This bit stays set while the calibration is in progress and is

cleared when the calibration sequence is completed.

So I wonder why the loop is not checking for CAL flag instead, anyway.  Like this:

while (ADC_SC3_REG(adcmap) & ADC_SC3_CAL_MASK); // Wait calibration end

0 Kudos

Contributor II


It's rather late and I think you fixed it in the meantime but maybe someone runs over the same problem again. In my case, the problem occurred when I added the FreeRTOS to the project. It happened that the program sometimes got stucked in line 11:

while ( (ADC_SC1_REG(adcmap,A) & ADC_SC1_COCO_MASK ) == COCO_NOT );      // Wait calibration end

I first thought that it is a hardware bug but by wrapping this line with:



to avoid interrupts, the problem was fixed. It seems that this hardware flag can somehow be reseted accidentally in an interrupt routine.

0 Kudos

Contributor IV

I have another one: Please observe that the above procedure turns on 32-fold averaging and leaves without turning it off again. That means you have to configure your ADC before calibration (to be shure calibration applies to the mode you want to use later), and you have to configure it once more after calibration to really get into the mode you want to use.

0 Kudos

Contributor IV

This reminds me of another post over unexpected freeze and my own experience is that those samples need work before they can be made into something useful. For example in the code you posted, if you waste some seconds to look at lines 18 and 31, you will find they are nonsense. Also lines 20..25 and 32..37 are bad coding that someone invented to avoid the volatile access order warning that should have been handled by a pragma directive.

If you look at the other parts of your code, eventually you will find that the watchdog did not get initialized and screws up your system unexpectedly, or some interrupt causes premature departure into code that cannot yet be used because some clock gates have not yet been enabled. In those samples the global interrupt enable is usually turned on early.

When you finally make the ADC calibration work reliably, you will wonder if noise depends on other activities in your KL25, and which parts to turn off during conversion in order to get clean measurements. This is the point i am currently trying to understand. I am coming from MSP430 and there it was clear that you should use the built-in asynchronous clock of the ADC while all other hardware is in a low power mode and inactive, except maybe a 32 KHz low power crystal for timekeeping. Now the MSP430 does not have the big pin MUXes of the Kinetis, so i guess with the Kinetis those problems must be even worse. But of course all this is guesswork.

0 Kudos