nRF52840 maintains high current consumption after stopping SAADC

INFO
-----------------------------------------
nRF52840 based custom board
nRF5 SDK v15.0.0
S140 SoftDevice v6.0.0
-----------------------------------------


Hello everyone,

We are developing an IoT device based on the nRF52840. The problem is that the power consumption of the device remains high even when all the peripherals participating in the sampling operation are turned off.

The application collects samples from four channels at a frequency of 100 Hz using TIMER and PPI instances. (TIMER+PPI+SAADC)

The problem explained in more detail:

Case 1: (for reference)
------------------------------

uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 625 * 16000); // 0.1 Hz     -->  (see the source code section!)

If TIMER is set to trigger an event via PPI in such a way that TIMER never reaches the comparison reading and thus SAADC never gets the sampling task, then the power consumption drops to the value (327 uA) after the closing peripherals is completed.



Case 2: (this is the case in the application)
-------------------------------------------------------

uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 625 * 16); // 100Hz Hz     -->  (see the source code section!)

If TIMER is set to trigger an event via PPI in such a way that TIMER reaches the comparison reading and thus SAADC gets the sampling task, then the power consumption drops to the value (1000 uA) after the closing peripherals is completed. That is, something remains ON in the background, because the power consumption does not drop as in case 1.



CODE: ( essential parts, main part modified for example)
--------------------------------------------------------------------------

void saadc_init(void)
{
    ret_code_t err_code;
    nrf_saadc_channel_config_t channel_config0 =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN5,NRF_SAADC_INPUT_AIN4);
    nrf_saadc_channel_config_t channel_config1 =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN6,NRF_SAADC_INPUT_AIN4);
    nrf_saadc_channel_config_t channel_config2 =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN7,NRF_SAADC_INPUT_AIN4);
    nrf_saadc_channel_config_t channel_config3 =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SGNL_VSEN);


    channel_config0.acq_time = NRF_SAADC_ACQTIME_3US;
    channel_config1.acq_time = NRF_SAADC_ACQTIME_3US;
    channel_config2.acq_time = NRF_SAADC_ACQTIME_3US;
    channel_config3.acq_time = NRF_SAADC_ACQTIME_3US;

    NRF_LOG_INFO("SAADC gain defaults (4/4/2/low)");
    channel_config0.gain = NRF_SAADC_GAIN4 ;
    channel_config1.gain = NRF_SAADC_GAIN4 ;
    channel_config2.gain = NRF_SAADC_GAIN2 ;
    channel_config3.gain = NRF_SAADC_GAIN1_4 ;

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

    err_code = nrf_drv_saadc_channel_init(0, &channel_config0);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(1, &channel_config1);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(2, &channel_config2);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(3, &channel_config3);
    APP_ERROR_CHECK(err_code);

    NRF_SAADC->CH[0].CONFIG |= 0x01000000;		// Burst mode ON
    NRF_SAADC->CH[1].CONFIG |= 0x01000000;		// Burst mode ON
    NRF_SAADC->CH[2].CONFIG |= 0x01000000;		// Burst mode ON
    NRF_SAADC->CH[3].CONFIG |= 0x01000000;		// Burst mode ON

    NRF_SAADC->OVERSAMPLE = 5;	//5;	//2^5 = 32
		
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

}

void saadc_sampling_event_init(void)
{
   ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    timer_cfg.interrupt_priority = 5;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);


    /* setup adc_timer for compare event every X ms */
//    uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 625* 16000);	// 0,1 Hz - only for testing
      uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 625 * 16);	//100Hz

	NRF_LOG_INFO(" ticks setting %ld", ticks);
        NRF_LOG_FLUSH();
        
	nrf_drv_timer_extended_compare(&m_timer,
                                   NRF_TIMER_CC_CHANNEL2,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK,
                                   false);
    nrf_drv_timer_enable(&m_timer);

	NRF_LOG_INFO(" timer enabled");
        NRF_LOG_FLUSH();


    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                NRF_TIMER_CC_CHANNEL2);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();


    NRF_LOG_INFO(" allocating PPI");
    NRF_LOG_FLUSH();

    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_INFO(" assigning PPI");
    NRF_LOG_FLUSH();

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
}

void saadc_sampling_event_enable(void)
{

    NRF_LOG_INFO(" SAADC sampling event enable");
    NRF_LOG_FLUSH();

    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    APP_ERROR_CHECK(err_code);
}

void saadc_sampling_event_uninit(void)
{
     ret_code_t err_code;

     nrf_drv_timer_disable(&m_timer);

     nrf_drv_timer_uninit(&m_timer);
 
}


void saadc_sampling_event_disable(void)
{

    nrf_drv_ppi_channel_disable(m_ppi_channel);

    nrf_drv_ppi_uninit();

}

void shutdown_periphreal_instances(void)
{
        uint32_t err_code;
        uint32_t *lxp;
        uint32_t  lx1;


        //disable TIMER(2), uninit TIMER(2), uninit PPI driver 
        //----------------------------------------------------
        saadc_sampling_event_uninit();


        // disable PPI channel, uninit PPI driver
        //----------------------------------------
        saadc_sampling_event_disable();


        // tested with these as well
        //----------------------------------------------
        // nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
        // nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
        // nrf_saadc_event_clear(NRF_SAADC_EVENT_END);


        // uninit SAADC 
        //-------------
        nrf_drv_saadc_abort();
        nrf_drv_saadc_uninit();
        
}
        
        
//======================================
// MAIN
int main(void)
{

    saadc_init();
    saadc_sampling_event_init();
    saadc_sampling_event_enable();
    
    
    shutdown_periphreal_instances();
    

    //=================================
    // MAIN While-loop.
    while(1)
    {

        idle_state_handle();

    }

}



I've found a few threads here on Nordic DevZone that have discussed the same topic, but I haven't found a solution to my problem of them.

Question 1: Can you help with this problem?



-----------------------------
URL1:
devzone.nordicsemi.com/.../how-to-stop-saadc

For example, this thread (URL1) has an almost identical problem, but the recommended solution does not work. It is mentioned in the thread that there is a bug with EasyDMA not releasing it's clock source, but it is only present when more than one channel is enabled.

Question 2: Do you know in which SDK version the bug mentioned in URL1 has been fixed?


I know that the HW (nRF52840 SOC) and SDK (nRF5 v15.0.0<) we use is not an optimal for developing battery powered IoT-device.

Question 3: Should we upgrade to a newer version of the nRF5 SDK or switch to the nRF connect SDK?

Related