Slow ADC

Hello,

I am using the ADC on my custom board, based on nRF9160.

I am getting correct results when measuring a known voltage. However, I noticed that the execution is very slow.

I am using the following code. Using a scope, I can see that it takes ~3.8ms.

#define ADC_BIT_RANGE			14
#define ADC_NB_SAMPLES_TAKEN	8

static const struct device *adc_dev = DEVICE_DT_GET(DT_INST(0, nordic_nrf_saadc));

static int16_t m_sample_buffer[ADC_NB_SAMPLES_TAKEN];



int ADC_MeasureMillivots_Blocking(uint8_t ch)
{
	uint8_t i ;
	int val_mv = -1 ; 
	bool success = false ;

	struct adc_sequence_options sequenceOption ={
		.callback = NULL,
		.extra_samplings = ADC_NB_SAMPLES_TAKEN-1 ,
		.interval_us = 0 ,
		.user_data = NULL 
	};

	struct adc_sequence sequence = {
			.options	 = &sequenceOption,
			.channels    = BIT(ch),	
			.buffer      = &m_sample_buffer,
			.buffer_size = sizeof(m_sample_buffer),
			.resolution  = ADC_BIT_RANGE,
			.oversampling = 0,
			.calibrate	 = false };

	
	struct adc_channel_cfg channelConfig = {
		.gain = ADC_GAIN_1_6,
		.reference = ADC_REF_INTERNAL,
		.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5),
		.channel_id = 1,							// Will be updated just a few line below
		.input_positive = 0 };	// Will be updated just a few line below


	// There are only 8 channels: AN0 to AN7
	if(ch <= 7)
	{
		// Exit low power mode
		pm_device_action_run(adc_dev, PM_DEVICE_ACTION_RESUME);		

		// Update the configuration of the ADC channel to read, according to what is requested in argument
		channelConfig.channel_id = ch ;
		channelConfig.input_positive = (1 + ch) ; // Channel are offet by 1 : CH0 is input 1, CH1 is input 2, ...
		
		// Update setup
		if(!adc_channel_setup(adc_dev, &channelConfig) )
		{
			// Read result
			if( !adc_read(adc_dev, &sequence) )
			{				
				success = true ;				
			}
		}
	}

	// Enter ADC low power
	pm_device_action_run(adc_dev, PM_DEVICE_ACTION_SUSPEND);

	// Convert the result
	if(success)
	{
		// Make an average of the samples
		val_mv = 0 ;
		for(i=0;i<ADC_NB_SAMPLES_TAKEN;i++){
			val_mv += m_sample_buffer[i] ;			
		}
		val_mv /= ADC_NB_SAMPLES_TAKEN ;

		// Convert to millivolts
		adc_raw_to_millivolts(adc_ref_internal(adc_dev), channelConfig.gain, sequence.resolution, &val_mv) ;
	}
	else
	{
	    // Error !
		val_mv = -1 ;
	}

	return val_mv ;
}

Device tree is as simple as :

&adc {
	status = "okay";
};

Removing the adc_read() reduces the function execution to ~10µs. It's obviously useless, as I don't get any results. But it shows that the other lines of code in this function have a very small impact on execution time.

I tried several things, but none of these made a significant difference:

- Remove the PM_DEVICE_ACTION_SUSPEND/RESUME

- Reduce the number of samples

- Reduce the resolution

- Change the acquisition time to 5, 10 or 20µs

On this topic, I notices that using the macro seems to provide strange results. File zephyr/drivers/adc.h states that range is 0-16383, but I see the following values :

> ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS,  5)  returns 16389
> ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10) returns 16394
> ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20) returns 16404

I am currently using NCS v2.5.2

Any hint on what can be wrong / how to improve speed ?

Thanks.

Parents
  • I haven't tried using the SAADC over Zephyr ADC driver, but as far as I know the Zephyr drivers only support rather basic usage and I wouldn't use it for anything that requires performance. I'd implement using the nrfx saadc driver or registers directly if that's your thing.

    We're using PPI to time differential sampling, set up with registers directly (mostly because we were more comfortable with that rather than using the library notation, though the difference is rather small as the macros make the thing slightly tedious to read), allows full use of the hardware support.

Reply
  • I haven't tried using the SAADC over Zephyr ADC driver, but as far as I know the Zephyr drivers only support rather basic usage and I wouldn't use it for anything that requires performance. I'd implement using the nrfx saadc driver or registers directly if that's your thing.

    We're using PPI to time differential sampling, set up with registers directly (mostly because we were more comfortable with that rather than using the library notation, though the difference is rather small as the macros make the thing slightly tedious to read), allows full use of the hardware support.

Children
No Data
Related