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

500uA current draw during System ON sleep mode using nRF52 DK, setting NRF_CLOCK_LF_SRC_XTAL throws error id 0x4001

Hello, I am developing an application with nRF52 DK. softdevice 112 and SDK V15.2.0. This is a "continuation" from my previous ticket (https://devzone.nordicsemi.com/f/nordic-q-a/64125/system-on-sleep-mode-problems-sometimes-500ua-draw-sometimes-7-5ma-draw-with-logging-and-debugging-disabled).

I successfully lowered my current draw from 7mA to 500uA. Now I need this sleep mode current to get even lower, as low as possible in the 2-3uA range or lower. I want to enter System ON sleep mode. I have found from previous devzone posts that 500uA draw in sleep mode can mean that the hf clock is running, when the lf clock should be used. However, I am using a soft device and it is my understanding that the softdevice should automatically handle clock management. I.E. use the HF clock during advertising/ble events and then switch to the LF clock when there is no ble activity and the antenna is not needed. I have tested that the lf clock is running by calling "nrf_clock_lf_is_running()" after I initialize the softdevice, and the function returns true which must mean the lf clock is indeed running. Here is what the power analysis looks like when using the PPK:

The brief protocol for my application is: turn on, read saadc data, turn off saadc, advertise, connect, send data, repeat. I do use nrf drv timer instance 1 while using the SAADC, however after I'm done reading data I disable and uninitialize this timer. When I am done reading SAADC data I disable and uninit the nrf drv timer, free the ppi channel (which also disables it), and uninit the saadc. I also clear and uninit some GPIO pins I use, and then uninit the gpiote peripheral itself. 

In my sdk_config.h, NRF_SDH_CLOCK_LF_SRC, NRFX_CLOCK_CONFIG_LF_SRC and CLOCK_CONFIG_LF_SRC are all set to 0 which means NRF_CLOCK_LF_SRC_RC. While the RC and Xtal lf clock options should be approximately the same (I've read 2-3% lower current draw using external lf xtal), I wanted to see if using the xtal would help at all. So, I change the sdk_config.h clock settings to 1 which means NRF_CLOCK_LF_SRC_XTAL. However when I do this, my application no longer runs and instead breaks with a fatal error. I have not changed the default configurations of SB1, 2, 3, and 4 on the nRF52 DK as they are by default setup to use the external xtals. I have also disabled all logging defines in my sdk _config.h as well as backend_rtt. Attached is a screenshot of the error, which is thrown when I check the return code from "nrf_pwr_mgmt_init()":

The error code is 0x7, and then the id is 0x4001. Error 0x7 indicates incorrect parameters, but the function doesn't even take any parameters. I think this could be unrelated to the power draw issue, but it is strange that when I set my lf clock source to xtal I get this behavior. I have tried calling "nrf_clock_lf_src_set()" in main, passing the xtal as the parameter, and this does not cause any error. However, the current draw stays the same. 

To get my current draw down to 2-3uA range during sleep, do I have to manually turn off the hf clock and "switch" to the lf clock? I use an rtc timer during sleep, using the app_timer module, which supposedly runs off rtc and lf clock. If I need to manually turn off the hf clock, can someone show me some examples on how to do this? Or, do I need to uninitialize some/all of the ble stack after I am done advertising/sending data and before I put the device to sleep? I can keep my device set to use the RC oscillator for the lf clock if this will still allow me to achieve 2-3uA draw, if that is helpful compared to trying to solve the above error. Any help here would be greatly appreciated. Thanks!

  • Hi Noah.

    You describe two problems and I'm not quite sure if they are unrelated or not.  Nevertheless, I would like to reproduce the issues on my end. Could you share a minimal project that reproduces the issues and can be flashed on the development kit?

    regards

    Jared 

  • It's going to be tough for me to create a minimal project. Would it work if I send you the main.c and sdk_config.h? I may have edited some source files slightly, like ble_advertising.c such that advertising doesn't automatically restart on disconnect. Maybe I can send a zip of the sdk that just contains my project? That might be the easiest way for you to try and replicate

  • void saadc_sampling_event_init(void)
    {
        ret_code_t err_code;
    
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
        err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
        APP_ERROR_CHECK(err_code);
    
        uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 35);
        nrf_drv_timer_extended_compare(&m_timer,
                                       NRF_TIMER_CC_CHANNEL0,
                                       ticks,
                                       NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                       false);
        nrf_drv_timer_enable(&m_timer);
    
        uint32_t timer_compare_event_addr = 
                    nrf_drv_timer_compare_event_address_get(&m_timer,
                                                            NRF_TIMER_CC_CHANNEL0);
        uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();
    
        /* setup ppi channel so that timer compare event triggers task in SAADC */
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                              timer_compare_event_addr,
                                              saadc_sample_task_addr);
        APP_ERROR_CHECK(err_code);
    }

    I have narrowed it down so that I know something in this function above is causing the 500uA draw. If I don't call this function, the sleep current is at 2uA. Once I call this function, current draw is at ~500uA. This 500uA draw happens even if I don't call nrf_drv_ppi_channel_enable() to actually start reading saadc values.

    void disable_pH_voltage_reading(void)
    {
        ret_code_t err_code;
        nrfx_timer_uninit(&m_timer);
        err_code = nrfx_ppi_channel_free(m_ppi_channel);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_saadc_channel_uninit(0);
        APP_ERROR_CHECK(err_code);
        nrfx_saadc_uninit();
        NVIC_ClearPendingIRQ(SAADC_IRQn);
        while(nrfx_saadc_is_busy()) {
            // make sure SAADC is not busy
        }
    }

    The above function is what I call to "turn off" the saadc, however this does not put the nRF52 DK back to low power 2uA current draw. 

  • I narrowed it down specifically to the call to nrf_drv_timer_enable(&m_timer); as the culprit for driving the current up. Even though I try to uninit the timer in disable_ph_voltage_reading(), it does not change the current consumption. My workaround is to use the SAADC with blocking protocol, compared to the current buffer conversion/ppi channel etc protocol that I am using in the code provided here. When I use the blocking protocol, I don't have to explicitly init or enable an nrf_drv_timer and don't have the issue of 500uA current draw, I get expected ~2uA draw in sleep mode.

    Would really like some help figuring out why I can't fully disable the nrf drv timer instance 1 all the way, even though I am calling nrfx_timer_uninit().

  • Hi Noah,

    Noah said:
    Maybe I can send a zip of the sdk that just contains my project? That might be the easiest way for you to try and replicate

     This works too! You can upload the zipped SDK here. Could you also list any changes you've done to any file that's not main.c? 

    I can make the case private first if you prefer it. 

    Noah said:
    narrowed it down specifically to the call to nrf_drv_timer_enable(&m_timer); as the culprit for driving the current up. Even though I try to uninit the timer in disable_ph_voltage_reading(), it does not change the current consumption. My workaround is to use the SAADC with blocking protocol, compared to the current buffer conversion/ppi channel etc protocol that I am using in the code provided here. When I use the blocking protocol, I don't have to explicitly init or enable an nrf_drv_timer and don't have the issue of 500uA current draw, I get expected ~2uA draw in sleep mode.

    Thanks for a good explanation, I'll try to reproduce this. 

    regards

    Jared 

Related