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

Race condition with ADC driver

There is a race condition in the SAADC driver when using buffer convert.

I am trying to drive ADC at maximum rate to sample 3 ADC channels, and so I call the following in the interrupt routine when it returns from completing an ADC and filling the first buffer. I am using

#define BUFFER_SAMPLES 3

in my application

        /* set up next buffer */
        err_code = nrfx_saadc_buffer_convert(p_buffer, BUFFER_SAMPLES);
        APP_ERROR_CHECK(err_code);
        /* start next sample */
        err_code = nrfx_saadc_sample();
        APP_ERROR_CHECK(err_code);

When I use the following

            channel_config.acq_time = NRF_SAADC_ACQTIME_40US;

the sampling process freezes and does not do any ADC

However if I use

            channel_config.acq_time = NRF_SAADC_ACQTIME_3US;

everything works.

Also if I set

#define BUFFER_SAMPLES 1

everything works.

I did some debugging and noticed if I set breakpoints everything worked, and I iterated to find a critical place. Eventually I determined if I placed a for...loop at ***** in the following the driver would work for 40US.

There appears to be a race condition in setting the NRF_SAADC_TASK_SAMPLE when a long acquisition is set, ie before the first sample is taken and written to RAM (not sure why one sample is taken after issuing a START but looking at memory shows this is the case).

nrfx_err_t nrfx_saadc_sample()
{
        
    NRFX_ASSERT(saadc_m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);

    nrfx_err_t err_code = NRFX_SUCCESS;
    if (saadc_m_cb.adc_state != NRF_SAADC_STATE_BUSY)
    {
        err_code = NRFX_ERROR_INVALID_STATE;
    }
    else if (saadc_m_cb.low_power_mode)
    {
        nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
    }
    else
    {

******
        nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
    }

    NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
    return err_code;
}

I have a workaround by using 3US but it would be good for this issue to be fixed.

Parents
  • There are two issues here:

    1. You say you want the maximum sample rate, but you use a buffer that only holds a single sample for each adc channel, you need to increase to a more suitable number, otherwise, you will have a massive CPU overhead between each sample. 

    2. Why do you not use a TIMER and PPI to control the sample rate instead of triggering each sample manually via the CPU?

    Do you mind sharing more information on what you want to achieve with the adc?

  • Thank you for your response.

    I actually set up 3 separate channels to be sampled, and then I must process this data before sampling the same 3 channels again. I had a problem with Calibrate which was creating bogus samples, but this is fixed using the workaround in the errata sheet.

    The way the device works is to perform one sample on each channel and then give an interrupt. This is fine for my application, except the Calibrate was messing up the buffer and I went to one sample and tried to change the channel, which was the problem I reported her.

    Now I have fixed Calibrate, and realised I can use 3us and my application is acceptable.

    I could interleave sampling with double buffering but there is a feature in the SAADC driver that set SAADC status to idle after checking a STOP condition, which is required as the workaround for calibrate.

    As the application now works reasonably well, I am happy to leave as is.

    Malcolm

Reply
  • Thank you for your response.

    I actually set up 3 separate channels to be sampled, and then I must process this data before sampling the same 3 channels again. I had a problem with Calibrate which was creating bogus samples, but this is fixed using the workaround in the errata sheet.

    The way the device works is to perform one sample on each channel and then give an interrupt. This is fine for my application, except the Calibrate was messing up the buffer and I went to one sample and tried to change the channel, which was the problem I reported her.

    Now I have fixed Calibrate, and realised I can use 3us and my application is acceptable.

    I could interleave sampling with double buffering but there is a feature in the SAADC driver that set SAADC status to idle after checking a STOP condition, which is required as the workaround for calibrate.

    As the application now works reasonably well, I am happy to leave as is.

    Malcolm

Children
No Data
Related