NRF52840 running with BLE Advertising every 1 second consumes 450 uA even with tickless idle mode

We are running an NRF52840 on our custom board and getting around 450 uA while is BLE Advertising with a period of about 1 second.

If we disable BLE then our current consumption drops to 45 uA.

We would like to get current consumption down to 100 uA with BLE Advertising - is this possible?

Setup

- Freertos

- Tickless idle mode (using the nrf52 cmsis port code provided by nordic)

- 17.1 SDK

- NRF_LOGS is disabled

- no other peripherals running (QSPI is off ...etc)

Any suggestions on how to reduce current consumption while BLE is advertising? 

We don't want to use the SYSTEM OFF low power mode since we will eventually need other parts of our system to be active and not require a reset.

Here is a sample of the ble advertisement init code I'm running

static void advertising_init(void)
{
    uint32_t                    err_code;
    ble_advertising_init_t      init;

    // Service UUID list for advertising.
    ble_uuid_t m_adv_uuids[] =
    {
        {BLE_UUID_DATA_SERVICE, m_dcs.uuid_type}
    };

    std::memset(&init, 0, sizeof(init));

    // Fill main advertising data packet with complete data service UUID
    init.advdata.uuids_complete.uuid_cnt           = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    init.advdata.uuids_complete.p_uuids            = m_adv_uuids;
    init.advdata.flags                             = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    // Begin filling scan response packet with advertising name and flags
    init.srdata.name_type                          = BLE_ADVDATA_FULL_NAME;
    init.srdata.include_appearance                 = false;

    // Create variables for advertising service data
    ble_advdata_service_data_t  service_data;
    uint8_array_t               data_array;

    // Point at the data, add service data UUID
    data_array.p_data                              = (uint8_t*)blem_get_serial_number();
    data_array.size                                = blem_get_serial_number_bytes();
    service_data.service_uuid                      = 0x180A;
    service_data.data                              = data_array;

    // Add service data structure to init
    init.srdata.p_service_data_array               = &service_data;
    init.srdata.service_data_count                 = 1;

    init.config.ble_adv_whitelist_enabled          = false;
    init.config.ble_adv_directed_high_duty_enabled = false;
    init.config.ble_adv_directed_enabled           = false;
    init.config.ble_adv_directed_interval          = 0;
    init.config.ble_adv_directed_timeout           = 0;
    init.config.ble_adv_fast_enabled               = true;
    init.config.ble_adv_fast_interval              = APP_ADV_FAST_INTERVAL_UNITS; // 25 ms
    init.config.ble_adv_fast_timeout               = APP_ADV_FAST_DURATION_UNITS; // 30 seconds
    init.config.ble_adv_slow_enabled               = true;
    init.config.ble_adv_slow_interval              = APP_ADV_SLOW_INTERVAL_UNITS; // 1022 ms
    init.config.ble_adv_slow_timeout               = APP_ADV_SLOW_DURATION_UNITS; // 0 (run forever)

    init.evt_handler                               = on_adv_evt;

    err_code                                       = ble_advertising_init(&m_advertising, &init);
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}

  • That makes sense Joshua, so you can turnoff those peripherals that you turned on in bootloader before it jumps to the main application. 

  • Hi Susheel,

    Yes, we need to find out what's going on in our bootloader.

  • Hi Joshua,

    jthmp said:
    Also, as a note: it looks like calling 
    ble_stack_init(); 
    is enough to result in 450 uA of current. I don't even init advertising, or gap or anything else

    You wrote the above earlier and also wrote below yesterday

    jthmp said:
    If I erase the bootloader and just run the application directly I get 100 uA.

    I am unable to connect these two together to same source of issue. The softdevice will not reenable any peripherals based on bootloader is present or not. So I am a bit unsure if softdevice_enable had any role to play in this. For a moment, let us keep this aside and focus only on bootloader.

    To know which peripheral you have enabled in the bootloader, you might have to go and check the Enabled status register of each peripheral from the debugger. 

    Asking AI it gave a template code which might help to dump peripheral status runtime in your bootloader just before starting the application.

    in sdk_config.h, use RTT

    //------------------------------------------------------------------------------
    // <e> NRF_LOG_ENABLED - Enables/disable logging.
    //------------------------------------------------------------------------------
    /// 1: Enabled; 0: Disabled
    #define NRF_LOG_ENABLED 1
    
    // <o> NRF_LOG_DEFAULT_LEVEL  - Default Severity level for logging.
    //
    // <0=> Off
    // <1=> Error
    // <2=> Warning
    // <3=> Info
    // <4=> Debug
    #define NRF_LOG_DEFAULT_LEVEL 3
    
    // <e> NRF_LOG_BACKEND_RTT_ENABLED - If enabled the RTT logging backend will be compiled.
    //------------------------------------------------------------------------------
    /// 1: Enabled; 0: Disabled
    #define NRF_LOG_BACKEND_RTT_ENABLED 1
    // </e>
    
    // <e> NRF_LOG_BACKEND_SERIAL_USES_UART - If enabled the UART logging backend will be compiled.
    //------------------------------------------------------------------------------
    /// 1: Enabled; 0: Disabled
    #define NRF_LOG_BACKEND_SERIAL_USES_UART 0
    // </e>
    
    // <e> NRF_LOG_BACKEND_UART_ENABLED - Enable UART logging backend.
    //------------------------------------------------------------------------------
    /// 1: Enabled; 0: Disabled
    #define NRF_LOG_BACKEND_UART_ENABLED 0
    // </e>
    
    //------------------------------------------------------------------------------
    // <e> NRF_LOG_USES_TIMESTAMP - Use timestamping on log messages.
    //------------------------------------------------------------------------------
    /// 1: Enabled; 0: Disabled
    #define NRF_LOG_USES_TIMESTAMP 0
    // </e>
    

    In your bootloader have something like this to dump register status of every peripheral

    #include <stdbool.h>
    #include <stdint.h>
    #include "nrf.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "nrf_drv_clock.h"
    #include "app_error.h"
    
    /** 
     * @brief  Scan the most common nRF52 peripherals and log any that remain enabled.
     *         Call this as early as possible in main(), before any peripheral initialization.
     */
    static void dump_active_peripherals(void)
    {
        NRF_LOG_INFO("=== Peripheral ENABLE STATUS ===");
    
        // UART0
        if (NRF_UART0->ENABLE != UART_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("UART0 is ENABLED (0x%08X)", NRF_UART0->ENABLE);
        }
    
    #if defined(UARTE1)
        // UARTE1 (if your chip/board has it)
        if (NRF_UARTE1->ENABLE != UARTE_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("UARTE1 is ENABLED (0x%08X)", NRF_UARTE1->ENABLE);
        }
    #endif
    
        // SPIM0 / SPIM1 / SPIM2 / SPIM3
        if (NRF_SPIM0->ENABLE != SPIM_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("SPIM0 is ENABLED (0x%08X)", NRF_SPIM0->ENABLE);
        }
        if (NRF_SPIM1->ENABLE != SPIM_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("SPIM1 is ENABLED (0x%08X)", NRF_SPIM1->ENABLE);
        }
        if (NRF_SPIM2->ENABLE != SPIM_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("SPIM2 is ENABLED (0x%08X)", NRF_SPIM2->ENABLE);
        }
    #if defined(NRF_SPIM3)
        if (NRF_SPIM3->ENABLE != SPIM_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("SPIM3 is ENABLED (0x%08X)", NRF_SPIM3->ENABLE);
        }
    #endif
    
        // TWI0 / TWI1
        if (NRF_TWI0->ENABLE != TWI_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("TWI0 is ENABLED (0x%08X)", NRF_TWI0->ENABLE);
        }
        if (NRF_TWI1->ENABLE != TWI_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("TWI1 is ENABLED (0x%08X)", NRF_TWI1->ENABLE);
        }
    
        // TIMER0 / TIMER1 / TIMER2 (check ENABLE bit)
        if (NRF_TIMER0->ENABLE != TIMER_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("TIMER0 is ENABLED (MODE=0x%02X)", NRF_TIMER0->MODE);
        }
        if (NRF_TIMER1->ENABLE != TIMER_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("TIMER1 is ENABLED (MODE=0x%02X)", NRF_TIMER1->MODE);
        }
        if (NRF_TIMER2->ENABLE != TIMER_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("TIMER2 is ENABLED (MODE=0x%02X)", NRF_TIMER2->MODE);
        }
    
        // PWM0 / PWM1
        if (NRF_PWM0->ENABLE != PWM_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("PWM0 is ENABLED (0x%08X)", NRF_PWM0->ENABLE);
        }
    #if defined(NRF_PWM1)
        if (NRF_PWM1->ENABLE != PWM_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("PWM1 is ENABLED (0x%08X)", NRF_PWM1->ENABLE);
        }
    #endif
    
        // SAADC
        if (NRF_SAADC->ENABLE != SAADC_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("SAADC is ENABLED (0x%08X)", NRF_SAADC->ENABLE);
        }
    
        // RNG: check if PSELCLK ≠ 0xFFFFFFFF (means RNG was started/configured)
        if (NRF_RNG->PSEL.PSELCLK != 0xFFFFFFFF) {
            NRF_LOG_INFO("RNG is CONFIGURED (PSELCLK=0x%08X)", NRF_RNG->PSEL.PSELCLK);
        }
    
        // RTC0 / RTC1 (check ENABLE bit)
        if (NRF_RTC0->ENABLE != RTC_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("RTC0 is ENABLED (PRESCALER=%u)", NRF_RTC0->PRESCALER);
        }
        if (NRF_RTC1->ENABLE != RTC_ENABLE_ENABLE_Disabled) {
            NRF_LOG_INFO("RTC1 is ENABLED (PRESCALER=%u)", NRF_RTC1->PRESCALER);
        }
    
        // CLOCK: HFCLK / LFCLK status
        if (NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) {
            NRF_LOG_INFO("HFCLK is RUNNING (0x%08X)", NRF_CLOCK->HFCLKSTAT);
        }
        if (NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) {
            NRF_LOG_INFO("LFCLK is RUNNING (0x%08X)", NRF_CLOCK->LFCLKSTAT);
        }
    
        NRF_LOG_INFO("==================================");
    }
    
    
    void exiting_bootloader()
    {
        // 1) Initialize the logging (RTT backend in this example)
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        // 2) Temporarily start the low-frequency clock so that the CPU can sleep.
        //    (Otherwise HFCLKSTAT / LFCLKSTAT reads might be invalid.)
        nrf_drv_clock_init();
        nrf_drv_clock_lfclk_request(NULL);
    
        // 3) Dump active peripherals **before** you enable anything else.
        dump_active_peripherals();
    
        // 4) Enter an idle loop to push out logs and go to sleep between RTT interrupts
        while (true) {
            // Process any pending logs (RTT or UART). If none, sleep.
            if (!NRF_LOG_PROCESS()) {
                __WFE();
            }
        }
    }
    

    I have not tried this code myself but quick review shows that it is not that bad idea.

  • Might be pending FPU Exceptions. Roughly 0.5mA current seem likely.

    The non-RTOS samples in the SDK should all have code (called from idle_state_handler) that silences (skips) FPU exceptions in the hardware.

    I've not played much with FreeRTOS - no idea if that code piece made it into the Nordic examples for this OS.

    You want to call this from an "idle" thread that gets called every time the CPU is supposed to start sleeping.

    The trigger seems to be a bit random - its not unlikely that your bootloader somehow triggers conditions that would lead to an FPU exception...

Related