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?

  • Hi Iisko84,

    Question 1: Can you help with this problem?

    I tried to look at it for a bit but nothing comes to mind yet. I know of the issue in URL1 but you already found it.

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

    All SDK releases have a section for known issues and bug fixes.
    There is also a list compiled here: What are SDK 15.x.0 known issues .
    Having said that, I don't find the ADC issue in URL1 mentioned in those lists since v15.0.0 either... I will check with my colleagues on this.

    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.

    I think the SoC itself is fine. Depends on your definition of "optimal," nRF5 SDK v15.0.0 is or isn't, because most of the times, newer SDK version fix bugs in older ones.

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

    Unless you have a strong reason, such as the product is already in production, then you should go to the newest version of SDK available. 

    At the moment, it is the nRF Connect SDK, whose latest version at the moment is v2.3.0.
    The transition from the nRF5 SDK to NCS is pretty big, so for situation where the option is not viable, we also recommend at least use nRF SDK v17.1.0.
    Yet, there could be incompatibilities, for example, DFU solutions. For that, we recommend going to the latest minor version, v15.3.0.

    I will continue to investigate your issue and get back to you if I find anything. Meanwhile, do you think you can emulate the situation as close as possible, but with each test, remove one peripheral from the experiment, to narrow down on what peripheral is malfunctioning?

    For example, to rule out TIMER, replace it with a GPIO event; to rule out PPI, replace it with simpler TIMER interrupt triggered SAADC task.

    Hieu

  • Thank you for the quick reply Hieu.

    Yes, I can continue to investigate the problem in more detail in the way you suggested.

    I will also come back to this matter if I find out anything significant about this problem.

    -Iisko84-

  • Sure Iisko84, I will wait for your update.

    Do you see the same issue on a nRF52840 DK? I am a bit tight on time this week and the next, but if it can be reproduced on the nRF52840 DK, then I can try to reproduce it.

  • Yes, of course I can try to see if the same problem occurs with nRF52840 DK as well. I'm also busy with other projects, but I'm trying to find time for this case as well, so there's no need to rush with this problem. I understand that you have other jobs as well.

    -Iisko84-


  • Hello again after a long time,

    I have been working on other projects, but I was able to continue with this problem now.

    I browsed the DevZone and found a thread (URL) where this particular power consumption issue was fixed using the ERRATA 212 workaround.

    URL: devzone.nordicsemi.com/.../394916

    I tried the fix first with SDK version 15.0.0, but the fix didn't change the situation. Then I tried the fix with SDK version 17.1.0 and the fix worked. I did experiments with a modified SAADC example, so I did not port my own project from SKD version 15.0.0 to version 17.1.0

    Can you confirm that the ERRATA 212 fix is ​​not really working on SKD version 15.0.0? If this is the case, then isn't it also impossible to make a low-current multi-channel ADC project with SDK verio 15.0.0, because there's always an extra 600 uA current left in the background?

    ERRATA 212 FIX:

    void shutdown_periphreal_instances(void)
    {
            uint32_t err_code;
            uint32_t *lxp;
            uint32_t  lx1;
            uint32_t value;
            
    
            //disable TIMER(2), uninit TIMER(2), uninit PPI driver 
            //----------------------------------------------------
            saadc_sampling_event_uninit();
    
    
            // disable PPI channel, uninit PPI driver
            //----------------------------------------
            saadc_sampling_event_disable();
    
    
    
            // Errata 212 
            //--------------
            /* Applying workaround from Errata 212, otherwise current is stuck at 4-500uA during sleep after first sample. */
            volatile uint32_t temp1;
            volatile uint32_t temp2;
            volatile uint32_t temp3;
    
            temp1 = *(volatile uint32_t *)0x40007640ul;
            temp2 = *(volatile uint32_t *)0x40007644ul;
            temp3 = *(volatile uint32_t *)0x40007648ul;
    
            *(volatile uint32_t *)0x40007FFCul = 0ul; 
            *(volatile uint32_t *)0x40007FFCul; 
            *(volatile uint32_t *)0x40007FFCul = 1ul;
    
            *(volatile uint32_t *)0x40007640ul = temp1;
            *(volatile uint32_t *)0x40007644ul = temp2;
            *(volatile uint32_t *)0x40007648ul = temp3;
            
    
            // uninit SAADC 
            //-------------
            nrf_drv_saadc_abort();
            nrf_drv_saadc_uninit();
    
    }

Related