This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Multiple SAADC Samples

I'm having issue where after booting I can immediately do three SAADC samples as follows:

    for (uint8_t i = 0; i < SAMPLES_IN_BUFFER; i++)
    {
	nrfx_saadc_sample();
	nrf_delay_ms(SAADC_SAMPLE_DELAY);
    }

This works as expected where I get the callback immediately after the third call to nrfx_saadc_sample(). I needed to put in a 1ms delay, otherwise the first sample is 0; However, when I do the same thing in a timer handler, I cannot do three samples in a row. The timer needs to timeout three times in order to get the three samples. I doesn't matter how long the delay time is (I've put in up to 1 second), the only way to get three samples is for the handler to trigger three times. I'm using an app_timer. Here is the handler code:

static void advertise_timeout_handler(void * p_context)
{
    for (uint8_t i = 0; i < SAMPLES_IN_BUFFER; i++)
    {
	nrfx_saadc_sample();
	nrf_delay_ms(SAADC_SAMPLE_DELAY);
    }
}

Here is the SAADC initialization code:

static void saadc_init(void)
{
    ret_code_t              err_code;
    nrf_drv_saadc_config_t  saadc_config;

    saadc_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE;
    saadc_config.resolution = NRF_SAADC_RESOLUTION_10BIT;	//Set SAADC resolution to 10-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^10=1024 (when input voltage is 3.6V for channel gain setting of 1/6).
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;    //Disable oversample. Can't oversample with more than one channel active.
    saadc_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY;
    nrf_saadc_channel_config_t channel_config =
    NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);

    err_code = nrf_drv_saadc_init(NULL, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channel_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);
}

Here is the callback code:

static void saadc_callback(nrf_drv_saadc_evt_t const *p_event)
{
    static uint32_t m_adc_evt_counter;
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
	ret_code_t err_code;
	uint32_t voltage;
	uint16_t average_sample = 0;
	err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
	APP_ERROR_CHECK(err_code);

	SEGGER_RTT_printf(0, "\nADC event number: %d\n", m_adc_evt_counter);

	for (uint8_t i = 0; i < SAMPLES_IN_BUFFER; i++)
	{
	    voltage = p_event->data.done.p_buffer[i] * 3600 / 1024;

	    SEGGER_RTT_printf(0, "%d\n", voltage);
	    average_sample += voltage;
	}

	if((++m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)
	    m_saadc_calibrate = true;

	battery_voltage = average_sample / SAMPLES_IN_BUFFER;
	change_manuf_specific_data();
    }
}

Why does the timer handler have to trigger three times in order to get three samples when outside of the hander after booting, I can do three samples in row and get a valid result?

  • Yes, I'm receiving three NRF_SUCCESS's each time the handler is executed, but the callback isn't triggered until the handler is executed three times. It's as if it the _sample triggering is only executed once and it ignores the other two. When the callback is finally triggered, I get three valid readings that are slightly different from each other as expected. Again, outside of the handler immediately after boot I do three successive _sample triggers and I get a callback right away, but the same exact method doesn't work in the handler.

    Yes, I've tried the SAADC peripheral example previously, but abandoned it as that timer uses much too much current. That's why I'm using the app_timer for all of my timers. 

  • I now have it working by pulling the _sample triggering out of the timer handler and just setting a flag within the handler. The successive _sample triggers are then executed in the main loop based on the flag. It seems as if the timer handler doesn't like multiple _sample triggers within the handler.

  • Hello,

    fackbell said:
    I now have it working by pulling the _sample triggering out of the timer handler and just setting a flag within the handler. The successive _sample triggers are then executed in the main loop based on the flag. It seems as if the timer handler doesn't like multiple _sample triggers within the handler.

    I am happy to hear that it is working now, but this behavior seems very strange to me still. If you are receiving NRF_SUCCESS from the _sample calls then the buffer should also be filled with 1 sample per success, and immediately after getting 3 you should get the DONE event.
    Do you have a stripped down version of the project, which showcases this particular behavior?
    If so, then I could take a look at it here on my end, and see if I cant figure out why it is behaving like this.

    fackbell said:
    Yes, I've tried the SAADC peripheral example previously, but abandoned it as that timer uses much too much current. That's why I'm using the app_timer for all of my timers. 

    I understand. Are you using the app_timer for any other tasks than the sampling? If not, you could tie it up to the SAADC as mentioned using PPI - since all Capture/compare events will be valid for the SAADC to begin sampling.

    Best regards,
    Karl 

Related