Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SAADC double buffering not working

Hi,

I'm setting up SAADC as explained in the examples: First start conversions with buffer 0, then with buffer 1. On the SAADC callback I queue the current buffer for conversions again.

I'm working on an NRF52832 using SDK15.2.

NRF_LOG_INFO("ADC samples[0] pointer = %p", &m_adc_samples[0]);
NRF_LOG_INFO("ADC samples[1] pointer = %p", &m_adc_samples[1]);

// Start conversions with buffer 0
err_code = nrf_drv_saadc_buffer_convert((nrf_saadc_value_t *)(&m_adc_samples[0]), get_adc_sampling_size());
APP_ERROR_CHECK(err_code);

// Queue conversions with buffer 1
// Should start immediately after buffer 0 is full
err_code = nrf_drv_saadc_buffer_convert((nrf_saadc_value_t *)(&m_adc_samples[1]), get_adc_sampling_size());
APP_ERROR_CHECK(err_code);

static void saadc_callback(nrf_drv_saadc_evt_t const * p_event) {
	if (p_event->type == NRF_DRV_SAADC_EVT_DONE) {

		static uint32_t sampling_count = 0;
		if ((sampling_count % SAADC_CALIBRATION_INTERVAL) == 0) {
			saadc_calibrate();
		}
		sampling_count++;

		NRF_LOG_INFO("Current buffer pointer = %p", p_event->data.done.p_buffer);
		
		// Queue conversions on the current buffer again
		// Conversions on the other buffer should be ongoing while we are in the callback
		ret_code_t err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, get_adc_sampling_size());
		APP_ERROR_CHECK(err_code);

		// Do something with data here
	}
}

When I start the conversions I see the following output:

ADC samples[0] pointer = 0x20005EE4
ADC samples[1] pointer = 0x2000613C

And then on the callbacks I see this:

Current buffer pointer = 0x20005EE4
Current buffer pointer = 0x2000613C
Current buffer pointer = 0x2000613C
Current buffer pointer = 0x2000613C
.
.
.

I would expect the logs on the callbacks to look like this:

Current buffer pointer = 0x20005EE4
Current buffer pointer = 0x2000613C
Current buffer pointer = 0x20005EE4
Current buffer pointer = 0x2000613C
Current buffer pointer = 0x20005EE4
Current buffer pointer = 0x2000613C
.
.
.

What am I doing wrong? I read the documentation and a bunch of threads in the devzone but I still don't see my problem, maybe I have tunnel vision already.

Thanks for your help!

EDIT: After reading a couple more threads I realized I needed to provide more information. Apparently the issue has something to do with the way the START and SAMPLE tasks are triggered.

I'm sampling more than one channel and I'm using PPI to trigger the SAMPLE task when a COMPARE0 event happens on a timer. As far as I can see, the START task is triggered by the nrfx_saadc module automatically when the low power mode is not configured (my case). I'm having the buffering problem even on first initialization, no need to stop and start to get the problem.

  • Ok, I tracked the issue down and I found a bug in the NRFX SAADC module. The issue happens when you setup everything for double buffering, but the ADC actually finishes the queued conversion before queueing the new conversion in the callback. See the logs below:

    ADC samples[0] pointer = 0x20005EE4
    ADC samples[1] pointer = 0x2000613C
    
    Channel initialized: 0.
    
    // First call
    Function: nrfx_saadc_buffer_convert, p_buffer: 0x20005EE4
    Function: nrfx_saadc_buffer_convert, buffer length: 5, active channels: 1.
    Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    
    // Second call
    Function: nrfx_saadc_buffer_convert, p_secondary_buffer: 0x2000613C
    Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    
    // NRFX SAADC IRQ handler end of conversion in buffer at 0x20005EE4
    Event: NRF_SAADC_EVENT_END.
    Copied p_secondary_buffer into p_buffer = 0x2000613C
    Triggering START task from IRQ handler - p_buffer = 0x2000613C
    
    // Pointer returned to my user callback
    Current buffer pointer = 0x20005EE4
    
    // Convert call from my user callback - properly setting up the secondary buffer
    Function: nrfx_saadc_buffer_convert, p_secondary_buffer: 0x20005EE4
    Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    
    // NRFX SAADC IRQ handler end of conversion in buffer at 0x2000613C
    Event: NRF_SAADC_EVENT_END.
    Copied p_secondary_buffer into p_buffer = 0x20005EE4
    Triggering START task from IRQ handler - p_buffer = 0x20005EE4
    
    // Pointer returned to my user callback
    Current buffer pointer = 0x2000613C
    
    // Convert call from my user callback - no longer setting up the secondary buffer
    Function: nrfx_saadc_buffer_convert, p_buffer: 0x2000613C
    Function: nrfx_saadc_buffer_convert, buffer length: 5, active channels: 1.
    Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    
    // NRFX SAADC IRQ handler end of conversion in buffer at 0x20005EE4
    Event: NRF_SAADC_EVENT_END.
    IRQ handler - Secondary buffer NULL
    
    //----------
    // BUG HERE
    //----------
    // Pointer returned to my user callback - wrong pointer, should be 0x20005EE4
    Current buffer pointer = 0x2000613C
    
    // Convert call from my user callback - starting conversion on the wrong buffer, because a wrong pointer was returned
    Function: nrfx_saadc_buffer_convert, p_buffer: 0x2000613C
    Function: nrfx_saadc_buffer_convert, buffer length: 5, active channels: 1.
    Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.

    I'll post the fix as soon as I figure out how to fix it.

  • Ok, the problem was that I was triggering the calibration task directly without using the driver and that was bringing the driver into an unwanted state.

  • Hello, I'm on the same problem now...

    Double buffering doesn't work except the initial state.

    How did you fix the problem?

Related