SAADC Oversample acquisition time

I have been doing some measurements on the execution time of my code and noticed something strange.

Currently, I am using an application timer to trigger the ADC to sample at 256Hz. 

The code to trigger the ADC in the application timer interrupt handler is shown below, 

Timer Interrupt Handler and Snippet of the function to trigger ADC sampling

( ignore the third ADC channel which is sampled at a different freq and not enabled currently )

static void timer_timeout_handler(void * p_context)
{
	adc_sample();
  LED_drv_timer_update();
}

static void adc_sample()
{
	int timer1 = app_timer_cnt_get();
	temp_cnt_tck++;
	int16_t temp[5] = {10};
	if( !m_saadc_initialized )
	{
		*((volatile uint32_t *)0x40007FFC) = 1;
		saadc_init2();
		if( ( (temp_cnt_tck % (tempSR/TEMP_OVER) ) == 0) && (temp_on) && (temp_cnt_tck>0))
		{
			NRF_SAADC->CH[2].PSELP = ANALOG_TEMPERATURE_SENSOR_PIN << SAADC_CH_PSELP_PSELP_Pos;
			NRF_SAADC->CH[2].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;
			NRF_SAADC->RESULT.MAXCNT = 3;
		}
		m_saadc_initialized = true;
	}
	saadc_sample();
	
	int timer2 = app_timer_cnt_get();
	int timer_d;
	app_timer_cnt_diff_compute(timer2,timer1,&timer_d);
	NRF_LOG_INFO("Ticks %d %d %d\n",timer1, timer2,timer_d);
	NRF_LOG_FLUSH();
	.
    .
    .
    .
}

SAADC Task Trigger ( taken from the forum )

ADC is reset to address the issue with high DMA current event after un-initialization when

bust + oversample + scan mode is enabled

static void saadc_sample()
{

  NRF_SAADC->TASKS_START = 1;
  while (NRF_SAADC->EVENTS_STARTED == 0);
  NRF_SAADC->EVENTS_STARTED = 0;
	
  // Do a SAADC sample, will put the result in the configured RAM buffer.
  NRF_SAADC->TASKS_SAMPLE = 1;
  while (NRF_SAADC->EVENTS_END == 0);
  NRF_SAADC->EVENTS_END = 0;
  NRF_SAADC->TASKS_STOP = 1;
	saadc_uninit2();
	*((volatile uint32_t *)0x40007FFC) = 0;
	m_saadc_initialized = false;
}

SAADC Configuration ( taken from the forum )

static void saadc_init2(void)
{
  // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain.
  NRF_SAADC->CH[0].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain4    << SAADC_CH_CONFIG_GAIN_Pos) |
                            (SAADC_CH_CONFIG_MODE_Diff       << SAADC_CH_CONFIG_MODE_Pos) |
                            (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                            (SAADC_CH_CONFIG_RESN_Bypass    << SAADC_CH_CONFIG_RESN_Pos) |
                            (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                            (SAADC_CH_CONFIG_TACQ_40us        << SAADC_CH_CONFIG_TACQ_Pos)	|
														(SAADC_OVERSAMPLE_OVERSAMPLE_Over4x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos)|
														(SAADC_CH_CONFIG_BURST_Enabled   << SAADC_CH_CONFIG_BURST_Pos);
  // Configure the SAADC channel with VDD as positive input, no negative input(single ended).
  NRF_SAADC->CH[0].PSELP = ANALOG_CH0_PIN << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[0].PSELN = ANALOG_VREF_PIN << SAADC_CH_PSELN_PSELN_Pos;
	
	NRF_SAADC->CH[1].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain4    << SAADC_CH_CONFIG_GAIN_Pos) |
                            (SAADC_CH_CONFIG_MODE_Diff       << SAADC_CH_CONFIG_MODE_Pos) |
                            (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                            (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos) |
                            (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                            (SAADC_CH_CONFIG_TACQ_40us        << SAADC_CH_CONFIG_TACQ_Pos)|
														(SAADC_OVERSAMPLE_OVERSAMPLE_Over4x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos)|
														(SAADC_CH_CONFIG_BURST_Enabled   << SAADC_CH_CONFIG_BURST_Pos);

  // Configure the SAADC channel with VDD as positive input, no negative input(single ended).
  NRF_SAADC->CH[1].PSELP = ANALOG_CH1_PIN << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[1].PSELN = ANALOG_VREF_PIN2 << SAADC_CH_PSELN_PSELN_Pos;
		
	NRF_SAADC->CH[2].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos) |
                            (SAADC_CH_CONFIG_MODE_SE        << SAADC_CH_CONFIG_MODE_Pos) |
                            (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                            (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos) |
                            (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                            (SAADC_CH_CONFIG_TACQ_40us        << SAADC_CH_CONFIG_TACQ_Pos)|
														(SAADC_OVERSAMPLE_OVERSAMPLE_Over4x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos)|
														(SAADC_CH_CONFIG_BURST_Enabled  << SAADC_CH_CONFIG_BURST_Pos);

//  // Configure the SAADC channel with VDD as positive input, no negative input(single ended).
  NRF_SAADC->CH[2].PSELP = SAADC_CH_PSELP_PSELP_NC << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[2].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;

  // Configure the SAADC resolution.
  NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_12bit << SAADC_RESOLUTION_VAL_Pos;

  // Configure result to be put in RAM at the location of "result" variable.
  NRF_SAADC->RESULT.MAXCNT = 2;
  NRF_SAADC->RESULT.PTR = (uint32_t)&result[0];

  // No automatic sampling, will trigger with TASKS_SAMPLE.
  NRF_SAADC->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task << SAADC_SAMPLERATE_MODE_Pos;

  // Enable SAADC (would capture analog pins if they were used in CH[0].PSELP)
	NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;
  

here I am trying to measure the time taken for the saadc_sample function to execute, which is

is a blocking function that waits for the 2 channels to finish sampling ( oversampling 4x, 40us acquisition time, burst enabled on all channels ).

I noticed that the time taken is the same ( 3 ticks = approx 90us ) regardless of the oversampling configuration, whereas intuitively the time taken should

be 2 ( channel count ) * 40us ( acquisition time ) * 4 ( oversample ) = 320us. 

Is this normal or is there something that I am missing?

Output from RTT 

Thanks!

  • To see if oversampling was working, I set a pin to toggle in the ADC interrupt on done event and it was toggling only at the frequency set by the RTC. Reading the register as you have suggested, I found that I wasn't setting the oversample register but instead overwriting to the first few bits of the config register with NRF_SAADC->CH[1].CONFIG = { SAADC_OVERSAMPLE_OVERSAMPLE_Over4x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos }.  

    After changing it, I can see now the pin is being toggled correctly.

    Thanks for the help!

  • Ah, I didn't notice in the previous replies, but the oversample configurations doesn't belong to the NRF_SAADC->CH[n].CONFIG registers. It belongs to the NRF_SAADC.OVERSAMPLE register.

    By writing SAADC_OVERSAMPLE_OVERSAMPLE_Over4x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos = 0x02 << 0 = 0x02 you are setting a pullup resistor on the GPIO on CH[n].

    Try setting the oversampling setting to the oversample register:
    NRF_SAADC->OVERSAMPLE = (SAADC_OVERSAMPLE_OVERSAMPLE_Over4x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos);

Related