SAM D21 ADC Correction Enable

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi:

 

   We have been using the ADC (from ASF) with our own linearity and bias offsets, but noticed that these values are already stored in NVM on the SAMD21's, so we wanted to try using them.  We grab the values from NVM and try to write them to

ADC->CALIB.bit.LINEARITY_CAL  and ADC->CALIB.bit.BIAS_CAL, then enable the correction with ADC->CTRLB.bit.CORREN, but doing so floors our ADC readings to zero.  Has anyone seen this behavior?  It seems to be the same if we write the registers directly OR use the ASF config, as shown in this code.

 

 

 	struct adc_config config_adc;
 	
 	uint32_t adc_linearity = (*((uint32_t *)(NVMCTRL_OTP4) // original position
 	+ (NVM_ADC_LINEARITY_POS / 32)) // move to the correct 32 bit window, read value
 	>> (NVM_ADC_LINEARITY_POS % 32)) // shift value to match the desired position
 	& ((1 << NVM_ADC_LINEARITY_SIZE) - 1); // apply a bitmask for the desired size
 
 	uint32_t adc_biascal = (*((uint32_t *)(NVMCTRL_OTP4)
 	+ (NVM_ADC_BIASCAL_POS / 32))
 	>> (NVM_ADC_BIASCAL_POS % 32))
 	& ((1 << NVM_ADC_BIASCAL_SIZE) - 1);
	
	// write values to CALIB register here, or below
	ADC->CALIB.bit.LINEARITY_CAL = adc_linearity;
	ADC->CALIB.bit.BIAS_CAL = adc_biascal;	
	ADC->CTRLB.bit.CORREN =1;
	
	adc_get_config_defaults(&config_adc);
	config_adc.clock_source = GCLK_GENERATOR_3;
	config_adc.reference = ADC_REFERENCE_INTVCC1;
    config_adc.accumulate_samples = ADC_ACCUMULATE_SAMPLES_1024;
	config_adc.divide_result = ADC_DIVIDE_RESULT_2;
	config_adc.resolution = ADC_RESOLUTION_CUSTOM;
	config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2;
	config_adc.positive_input = ADC_POSITIVE_INPUT_PIN16;
	config_adc.differential_mode = true;
	//OR if we do it here - 
	//config_adc.correction.correction_enable = true;
	//config_adc.correction.gain_correction = adc_linearity;
	//config_adc.correction.offset_correction = adc_biascal;
	adc_init(&adc_lightsensor, ADC, &config_adc);
	adc_enable(&adc_lightsensor);
	
	adc_start_conversion(&adc_lightsensor);
	while (adc_read(&adc_lightsensor, &lightreading) != STATUS_OK);
	

This works with our own calibration so it's not critical, but I'd like to use the built-in stuff wherever possible.

 

thanks

d

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Correction and calibration are not the same thing.

Always load the calibration data (this is done for you by the ASF 3 adc driver btw) but note it has nothing to do with ADC->CTRLB.bit.CORREN, i.e., that bit is to be set when you have loaded GAINCORR and OFFSETCORR (or done for you by ASF when you set correction_enable true and the correction values).

The correction values are signed 12-bit values, not even the same format as the calibration data.

/Lars 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah thanks, I just went and found where the factory calibration data is loaded.  I hadn't noticed that before.

 

What's confusing to me is your statement that "Correction and calibration are not the same thing"  can you expand on that a bit?  In practice, I feel like the calibration data should be enough for us to get a good ADC Zero point, is that not true?  We're still seeing a relatively high non-zero value (20ish points on a 12 bit ADC) when measuring a grounded pin... which says to me that the factory calibration data isn't enough.  I'd like to understand how to properly fill in the correction values, but having a hard time finding documentary.

 

thanks

again!

Dan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I mean applying the calibration data from NVM is expected always. If you also use correction depends on the application, e.g., with a single external sensor or circuit to measure it might be convenient (still need a way to compute/perform a calibration the get the correction values). If there are multiple inputs all needing different corrections it might be simpler to do all of that is SW (there is usually at least scaling to be performed anyway). 20 ish points zero offset sounds a bit much, is there noise maybe or do you have that after averaging also?

/Lars