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

SAADC Driver Issues

I have run into two SAADC issues on the nRF52832 using SDK v12.1.0. Any recommendations on how to fix them?

Issue 1: When the Keil compiler optimization is turned up to level 3 (-O3), the nrf_drv_saadc_abort() function always times out. This is due to the compiler assuming that m_cb.adc_state will not change in the while loop at line 479. What would be the recommended fix?

while ((m_cb.adc_state != NRF_SAADC_STATE_IDLE) && (timeout > 0))
{
    --timeout;
}
ASSERT(timeout > 0);

Issue 1 Fix: Declare the m_cb.adc_state member as volatile.


Issue 2: I am using the SAADC driver in low power mode and I am double-buffering my results. I set up my double-buffering by calling nrf_drv_saadc_buffer_convert() twice. My buffer size is 1 and I have 1 active channel. I call nrf_drv_saadc_sample() once and I correctly receive a SAADC_EVT_DONE event. When I call nrf_drv_saadc_sample() again, the SAADC_EVT_DONE event is never sent.

I've traced the issue to the following code in the SAADC_IRQHandler (line 101). For the first sample, the last case is performed and conversions_end is set to true and buffer_size_left is set to zero. For the second sample, the first case is performed, which does not set conversions_end to true and also also causes a memory overflow. What would be the recommended fix?

if (m_cb.buffer_size_left == 0)
{
    // Sampling finished, next buffer in progress.
    m_cb.buffer_size_left = m_cb.buffer_size - m_cb.active_channels;
    nrf_saadc_buffer_init((nrf_saadc_value_t *)&m_cb.p_buffer[m_cb.buffer_size -
                                                              m_cb.buffer_size_left],
                          m_cb.active_channels);
}
else if (m_cb.buffer_size_left > m_cb.active_channels)
{
    // More samples to convert than for single event.
    m_cb.buffer_size_left -= m_cb.active_channels;
    nrf_saadc_buffer_init((nrf_saadc_value_t *)&m_cb.p_buffer[m_cb.buffer_size -
                                                              m_cb.buffer_size_left],
                          m_cb.active_channels);
}
else if ((m_cb.buffer_size_left == m_cb.active_channels) &&
         (m_cb.p_secondary_buffer != NULL))
{
    // Samples to convert for one event, prepare next buffer.
    m_cb.conversions_end  = true;
    m_cb.buffer_size_left = 0;
    nrf_saadc_buffer_init((nrf_saadc_value_t *)m_cb.p_secondary_buffer,
                          m_cb.active_channels);
}
else if (m_cb.buffer_size_left == m_cb.active_channels)
{
    // Samples to convert for one event, but no second buffer.
    m_cb.conversions_end  = true;
    m_cb.buffer_size_left = 0;
}

Example project that can demonstrate the two issues: saadc_issue.zip The project expects to be located in the examples/peripheral folder.

Issue 3: The function to start calibration (nrf_drv_saadc_calibrate_offset) enables the channel 1 high limit interrupt, along with the calibration done interrupt. This is an easy fix, but I post it for others benefit. The change is needed on line 451 of the original driver. The wrong enum was used for the interrupt enable function.

Here is the corrected function:

ret_code_t nrf_drv_saadc_calibrate_offset()
{
    ASSERT(m_cb.state != NRF_DRV_STATE_UNINITIALIZED);

    if (m_cb.adc_state != NRF_SAADC_STATE_IDLE)
    {
        return NRF_ERROR_BUSY;
    }

    m_cb.adc_state = NRF_SAADC_STATE_CALIBRATION;

    nrf_saadc_event_clear(NRF_SAADC_EVENT_CALIBRATEDONE);
    nrf_saadc_int_enable(NRF_SAADC_INT_CALIBRATEDONE);    // fixed bug, used to be NRF_SAADC_EVENT_CALIBRATEDONE
    nrf_saadc_task_trigger(NRF_SAADC_TASK_CALIBRATEOFFSET);
    return NRF_SUCCESS;
}

Thanks, Austin

Related