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

SAADSC event handler not called if buffer length > 1 (non-blocking mode)

Hi, 

My freertos based application (SDK14.2) calls battery monitoring procedure every few minutes .

I've initialized the SAADC

void BATT_SaadcInit(void)
{
	ret_code_t err_code;
	
	nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
	
    err_code = nrf_drv_saadc_init(NULL, BATT_SaadcCallback);
    APP_ERROR_CHECK(err_code);

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


void BATT_Init(BATT_EventCallback_t callback)
{

    ...
	BATT_SaadcInit();
	...
}

Within the function I call 

static nrf_saadc_value_t	BATT_SamplesBuff[BATT_AVG_FACTOR];

...
static void BATT_BattTimerHandler(TimerHandle_t xTimer)
{
	ret_code_t err_code;
	
    err_code = nrf_drv_saadc_buffer_convert(BATT_SamplesBuff, BATT_AVG_FACTOR);
    APP_ERROR_CHECK(err_code);

	err_code = nrf_drv_saadc_sample();
	APP_ERROR_CHECK(err_code);
}

When BATT_AVG_FACTOR ==1 I get the SAADC event handler called immediately.

However when setting BATT_AVG_FACTOR = 2 or more, it is never triggered...

Digging further I've noticed that SAADC_IRQHandler(void) in nrf_drv_saadc.c is called twice (even with BATT_AVG_FACTOR greater than 2).

In the 2nd time its called it follows the following route 

void SAADC_IRQHandler(void)
{
    if (nrf_saadc_event_check(NRF_SAADC_EVENT_END))     // Enters if block, but does not enter the nested if blocks
    {
        nrf_saadc_event_clear(NRF_SAADC_EVENT_END);     
        NRF_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_END));

        if (!m_cb.low_power_mode || m_cb.conversions_end)   // Doesn't enter
        {
            nrf_drv_saadc_evt_t evt;
            evt.type               = NRF_DRV_SAADC_EVT_DONE;
            evt.data.done.p_buffer = (nrf_saadc_value_t *)m_cb.p_buffer;
            evt.data.done.size     = m_cb.buffer_size;

            if (m_cb.p_secondary_buffer == NULL)
            {
                m_cb.adc_state = NRF_SAADC_STATE_IDLE;
            }
            else
            {
                m_cb.buffer_size_left   = m_cb.secondary_buffer_size;
                m_cb.p_buffer           = m_cb.p_secondary_buffer;
                m_cb.buffer_size        = m_cb.secondary_buffer_size;
                m_cb.p_secondary_buffer = NULL;
                if (!m_cb.low_power_mode)
                {
                    nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
                }
            }
            m_cb.event_handler(&evt);
            m_cb.conversions_end = false;
        }
    }
    if (m_cb.low_power_mode && nrf_saadc_event_check(NRF_SAADC_EVENT_STARTED))  // Does NOT enter
    {
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
        NRF_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_STARTED));

        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;
        }
        nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
        nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
    }
    if (nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE))       // Does NOT enter
    {
        nrf_saadc_event_clear(NRF_SAADC_EVENT_CALIBRATEDONE);
        NRF_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_CALIBRATEDONE));
        m_cb.adc_state = NRF_SAADC_STATE_IDLE;

        nrf_drv_saadc_evt_t evt;
        evt.type = NRF_DRV_SAADC_EVT_CALIBRATEDONE;
        m_cb.event_handler(&evt);
    }
    if (nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED))     // Going into the else block
    {
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
        NRF_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_STOPPED));
        m_cb.adc_state = NRF_SAADC_STATE_IDLE;
    }
    else    // Getting into here but do nothing as limit_flags == 0 
    {
        uint32_t          limit_flags = m_cb.limits_enabled_flags;
        uint32_t          flag_idx;
        nrf_saadc_event_t event;

        while (limit_flags)
        {
            flag_idx     = __CLZ(limit_flags);
            limit_flags &= ~((1UL << 31) >> flag_idx);
            event        = FLAG_IDX_TO_EVENT(flag_idx);
            if (nrf_saadc_event_check(event))
            {
                nrf_saadc_event_clear(event);
                nrf_drv_saadc_evt_t evt;
                evt.type                  = NRF_DRV_SAADC_EVT_LIMIT;
                evt.data.limit.channel    = LIMIT_EVENT_TO_CHANNEL(event);
                evt.data.limit.limit_type = LIMIT_EVENT_TO_LIMIT_TYPE(event);
                NRF_LOG_DEBUG("Event limit, channel: %d, limit type: %d.",
                                            evt.data.limit.channel,
                                            evt.data.limit.limit_type);
                m_cb.event_handler(&evt);
            }
        }
    }
}

What am I missing here ?

Thanks

  • Some more info - 

    • SAADC_CONFIG_LP_MODE == 1 - the behavior as described above
    • SAADC_CONFIG_LP_MODE == 0 
      • If BATT_AVG_FACTOR == 1, works as expected
      • If BATT_AVG_FACTOR == 2 , does NOT get to SAADC_IRQHandler() even once !
  • If you have an SAADC buffer with size of N you must call the sample function, nrf_drv_saadc_sample() N times before you get a callback. Note that you need a certain delay between the two calls.

     

    I once made a simple example (in SDK15.0.0 (!)) that triggers a sample task on a buttonpress. Take a look at it, and see if it makes any sense.

    saadc_pin_int_change.zip

    I used Keil and nRF52832, so if you use something else, you might need to add some pats and files to the project in order to compile.

     

    Alternatively, if I understand the point of BATT_AVG_FACTOR correctly, you can look into just setting this to 1, and use oversampling (set this in sdk_config.h).

    #define SAADC_CONFIG_OVERSAMPLE 0

    set this to N, and the SAADC module will do 2^N samplings and average it.

     

    Best regards,

    Edvin

Related