7 years ago.

using power management API such as sd_app_evt_wait()/NRF_POWER->SYSTEMOFF in Nordic NRF52.

One of the things we are noticing even with the simplest of applications, the power consumption seems pretty high with NRF52, even when I am assuming it is asleep. There are no BLE events processing, yet, the power consumption what I am presuming is the sleep period is close to 3.6 ma, way above the 7ua's as per their spec. So in the Nordic forums they mention using sd_app_evt_wait() function part of their power management API's, which I have incorporated as shown below in the code. I am not sure if this is the right way to invoke it or when it needs to be invoked in the mbed world and using the mbed BLE Api's. However if I do use the NRF_POWER->SYSTEMOFF option, the current usage drops to the levels that are pretty much close to the spec. Any guidance in how best to configure sleep cycles/power mgmt for a ble central and ble peripheral device is greatly appreciated.

BLE API and sd_app_evt_wait

#include <mbed.h>
#include "ble/BLE.h"
#include "nrf_soc.h"
#include <vector>

extern "C"
{
    #include "softdevice_handler.h"
}

//initialize an event queue loop.
static EventQueue eventQueue(16*32);

//serial debug
Serial  m_pc(USBTX, USBRX);

// Mask of reserved bits of the register ICSR in the System Control Block peripheral
// In this case, bits which are equal to 0 are the bits reserved in this register
#define SCB_ICSR_RESERVED_BITS_MASK     0x9E43F03F
#define FPU_EXCEPTION_MASK 0x0000009F

void PowerManage(void) {
    // ensure debug is disconnected if semihost is enabled....

    // Trigger an event when an interrupt is pending. This allows to wake up
    // the processor from disabled interrupts.
    SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
    
    /* Clear exceptions and PendingIRQ from the FPU unit */
    __set_FPSCR(__get_FPSCR()  & ~(FPU_EXCEPTION_MASK));
    (void) __get_FPSCR();
    NVIC_ClearPendingIRQ(FPU_IRQn);
    
    // If the SoftDevice is enabled, its API must be used to go to sleep.
    if (softdevice_handler_isEnabled()) {
        sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
        sd_app_evt_wait();
    } else {
        NRF_POWER->TASKS_LOWPWR = 1;

        // Note: it is not sufficient to just use WFE here, since the internal
        // event register may be already set from an event that occurred in the
        // past (like an SVC call to the SoftDevice) and in such case WFE will
        // just clear the register and continue execution.
        // Therefore, the strategy here is to first clear the event register
        // by using SEV/WFE pair, and then execute WFE again, unless there is
        // a pending interrupt.

        // Set an event and wake up whatsoever, this will clear the event
        // register from all previous events set (SVC call included)
        __SEV();
        __WFE();

        // Test if there is an interrupt pending (mask reserved regions)
        if (SCB->ICSR & (SCB_ICSR_RESERVED_BITS_MASK)) {
            // Ok, there is an interrut pending, no need to go to sleep
            return;
        } else {
            // next event will wakeup the CPU
            // If an interrupt occured between the test of SCB->ICSR and this
            // instruction, WFE will just not put the CPU to sleep
            __WFE();
        }
    }
}
void QueueBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    eventQueue.call(Callback<void()>(&context->ble, &BLE::processEvents));
    //context->ble.processEvents();
    eventQueue.call(&PowerManage);
}

void OnBleInitError(BLE &ble, ble_error_t error)
{
   printf("BLE Initialization Error Occurred with BLE Error: %d\n", error);
}

void OnBleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;
    
    if (error != BLE_ERROR_NONE) {
        OnBleInitError(ble, error);
        return;
    }

    /* Ensure that it is the default instance of BLE */
    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }
    
    //this is to enable dc to dc regualator, which is turned off by default.
    if(sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE) == NRF_SUCCESS) {
        printf("Enabled DC2DC regulator!\n");
    }
    PowerManage();
 }
 
 int main() {
    
    //turn of flow control..
    m_pc.set_flow_control(SerialBase::Disabled);
    m_pc.baud(115200);
   
    printf("BLECentral Application...\n");
   
    BLE &ble = BLE::Instance();
    ble.onEventsToProcess(QueueBleEventsProcessing);
       
    //initialize the BLE.
    ble.init(OnBleInitComplete);
    
    //this dispatches the events in the event queue until break dispatch is called.
    eventQueue.dispatch_forever();
 
    printf("Exiting BLECentral Application...\n");
    
    return 0;
}

Thanks, Ajay.

Question relating to:

The nRF52 Development Kit is a single-board development kit for Bluetooth Smart, ANT and 2.4GHz proprietary applications using the nRF52 Series SoC. This kit supports both development for nRF52832 SoCs.

2 Answers

7 years ago.

Hello Ajay,

At the moment the RTOS used by mbed doesn't implement a tickless mode and the idle task doesn't put the mcu to sleep. In other words the micro will be quickly waked up (less than 1ms) after the call to PowerManage and will continue to be awake.

The first thing to do to mitigate this is to put the micro to sleep when there is nothing to do (this will be the default behavior with the next release of mbed OS):

Thread::attach_idle_hook(&sleep);

With this little change, the mcu will go to sleep once there is nothing to do but it will still be wake up by the tick generator every ms. The only way to avoid this at the moment is to disable the RTOS by using a .mbedignore file at the root of your project

mbed-os/rtos/*
mbed-os/features/FEATURE_CLIENT/*
mbed-os/features/FEATURE_COMMON_PAL/*
mbed-os/features/FEATURE_UVISOR/*
mbed-os/features/frameworks/*
mbed-os/features/net/*
mbed-os/features/netsocket/*
mbed-os/features/storage/*

Two last thing:

  • as soon as you start using printf, it enable the uart peripheral which gets never disabled afterward and consume a lot of power.
  • If your design support the DCDC (like the NRF52_DK), you can call sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE); in the ble init callback.

Accepted Answer

Hi Vincent,

Thanks for taking the time to respond and providing a solution in detail. I just had some follow up questions.

1) Is this not the same issue you are talking about which seems to have been raised for NRF51, https://developer.mbed.org/forum/bugs-suggestions/topic/27164/ and according to them this is fixed, has this fix not been implemented for NRF52?

2) Should I be calling the powermanage function as shown in my code base, or just calling the Thread::attach_idle_hook(&sleep), would handle calling the hal_sleep() method implemented in the sleep.c file under the NRF5 targets?

3) I am using the online compiler to compile my project, can I still create the .mbedignore file at the root of my project? Also if I disable the RTOS I won't be able to use any of the RTOS features such as threads, mutexes etc right, since eventually I would be using those features in my project. Is there a fix that is coming in the near future mbed os 5 release that fixes all these issues and if so what is the expected timeline when these issues would be addressed and is there a different version of mbed os that I can use with NRF52, which doesn't have the issues you have described?

4) As far as the printf goes, can I disable the UART at the BLE INIT handler for example and will it take effect or should I not use printf at all any where in my code, so that the UART module doesn't get enabled?

Thanks, Ajay

posted by Ajay Kashyap 27 Feb 2017

Hi Ajay,

1) Should I be calling the powermanage function as shown in my code base, or just calling the Thread::attach_idle_hook(&sleep), would handle calling the hal_sleep() method implemented in the sleep.c file under the NRF5 targets?

No you shouldn't call directly call either your powermanage function or sleep. Let the system call this function for you by installing a hook for the idle thread with `attach_idle_hook`. Since the PowerManage function exposed earlier is identical to the sleep function in the NRF target (I've written it...) there is no needs to duplicate code here.

2) I am using the online compiler to compile my project, can I still create the .mbedignore file at the root of my project? Also if I disable the RTOS I won't be able to use any of the RTOS features such as threads, mutexes etc right, since eventually I would be using those features in my project. Is there a fix that is coming in the near future mbed os 5 release that fixes all these issues and if so what is the expected timeline when these issues would be addressed and is there a different version of mbed os that I can use with NRF52, which doesn't have the issues you have described?

You can push a .mbedignore file to your repo but it is not possible to create it from the web IDE. If you want to use RTOS features, don't disable it then, it is a good option to save power if you don't use it though. A tickless version of the RTOS is being worked on, I can't comment on the timeline.

3) As far as the printf goes, can I disable the UART at the BLE INIT handler for example and will it take effect or should I not use printf at all any where in my code, so that the UART module doesn't get enabled?

That's very dangerous, to disable the UART behind the back of the system, especially if you use printf afterward. I advise to not use printf in release code if it is not needed, for debug builds, that's is still often needed.

posted by Vincent (pan-) Coubard 27 Feb 2017

Thanks a lot Vincent for answering my questions. I am compiling it offline to see if that helps with the .mbedignore file. Couple of other questions I had around the BLE examples.

1) All of the mbed ble examples of mbed-os 5, seem to use the new mbed-events library, which primarily uses the EventQueue for processing BLE messages, unlike the earlier BLE examples, where a main thread will block on a infinite while loop and in the while loop we would call ble.waitforBleEvent() method. Isn't EventQueue event loop running as a part of the rtos thread? If I remove the rtos using the mbed ignore, I won't be able to reference EventQueue, is that correct assumption?

2) If I do remove the implementation EventQueue from my code, base switch the to earlier way of processing messages, i.e. block on a while loop and use waitforBLEEvent?

3) Also I came across this mbed issue, something of a similar issue you were talking about which seems to have been raised for NRF51, https://developer.mbed.org/forum/bugs-suggestions/topic/27164/ and according to them this is fixed os 5.2, is this fix not what what you were talking about regarding the tickless mode of the RTOS?

Thanks, Ajay

posted by Ajay Kashyap 27 Feb 2017

Hi Ajay,

1) All of the mbed ble examples of mbed-os 5, seem to use the new mbed-events library, which primarily uses the EventQueue for processing BLE messages, unlike the earlier BLE examples, where a main thread will block on a infinite while loop and in the while loop we would call ble.waitforBleEvent() method. Isn't EventQueue event loop running as a part of the rtos thread? If I remove the rtos using the mbed ignore, I won't be able to reference EventQueue, is that correct assumption?

No it is not a correct assumption, the event queue is designed to work with or without the RTOS.

2) If I do remove the implementation EventQueue from my code, base switch the to earlier way of processing messages, i.e. block on a while loop and use waitforBLEEvent?

If you do not use the EventQueue and the RTOS then you can directly use sleep no needs to use the function from BLE API which is less general and just wait for BLE events.

3) Also I came across this mbed issue, something of a similar issue you were talking about which seems to have been raised for NRF51, https://developer.mbed.org/forum/bugs-suggestions/topic/27164/ and according to them this is fixed os 5.2, is this fix not what what you were talking about regarding the tickless mode of the RTOS?

The sleep function was fixed on NRF targets to do what it should but not the rtos which still do nothing in the idle loop for mbed 5.3.x versions (it has been fixed on master). Tickless mode is another optimisation which would avoid wake up from the os tick generator when they are not needed.

posted by Vincent (pan-) Coubard 27 Feb 2017

Hi Vincent,

I tried everything you said, however the current consumption is at about 3.45 ma. this is my latest code, the code can't get simpler than this. Not sure what is making the power consumption so high. Also is NRF52 platform supported on earlier version of the mbed OS? If there is support for them, would I run into the same issue I am facing on the current mbed os 5?

simple_ble_code

#include <mbed.h>
#include "ble/BLE.h"
#include "nrf_soc.h"

//initialize an event queue loop.
static EventQueue eventQueue(16*32);


void QueueBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    eventQueue.call(Callback<void()>(&context->ble, &BLE::processEvents));
}

void OnBleInitError(BLE &ble, ble_error_t error)
{
   //printf("BLE Initialization Error Occurred with BLE Error: %d\n", error);
}

void OnBleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;
    
    if (error != BLE_ERROR_NONE) {
        OnBleInitError(ble, error);
        return;
    }

    /* Ensure that it is the default instance of BLE */
    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }
    
    //this is to enable dc to dc regualator, which is turned off by default.
    sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
 }
 
 int main() {
    
    //NRF_POWER->SYSTEMOFF = 1;
    Thread::attach_idle_hook(&sleep);
   
    BLE &ble = BLE::Instance();
    ble.onEventsToProcess(QueueBleEventsProcessing);
       
    //initialize the BLE.
    ble.init(OnBleInitComplete);
    
    //this dispatches the events in the event queue until break dispatch is called.
    eventQueue.dispatch_forever();
 
    //printf("Exiting BLECentral Application...\n");
    
    return 0;
}

Any thoughts on why this piece of code will consume 3.5 ma?

Thanks, Ajay

posted by Ajay Kashyap 28 Feb 2017

Hi Ajay,

Could you provide me the following informations:

  • version of mbed-os used
  • RTOS: enabled or not ?
posted by Vincent (pan-) Coubard 28 Feb 2017

Hi Vincent,

The version of mbed-os is 5.3.3 and I used the mbed ignore file as you had suggested in the root of my project. I was not sure if the online compiler was using my .mbedignore file, so i created an offline toolchain and then built it with .mbedignore file present, so I am guessing at this point the RTOS is not enabled. How can I confirm that, so I can be sure?

I am using the mbed compile command using the mbed-cli. toolchain, gcc-arm and target nrf52. The presence of .mbedignore in the root of the application should take into effect right?

Here is the o/p of build, based on this it doesn't seem to be honoring my .mbedignore folder, I see rtos and rtx files being linked as well in my build folder, that shouldn't be happening right?

Link: TestBLEPowerConsumption Elf2Bin: TestBLEPowerConsumption +-----+-+-++

Module.text.data.bss

+-----+-+-++

Fill173730
Misc27781248484
drivers50200
features/FEATURE_BLE161785540
hal504016
platform14924264
rtos6644
rtos/rtx5903204278
targets/TARGET_NORDIC238731402200
Subtotals7647226647416

+-----+-+-++ Allocated Heap: 41168 bytes Allocated Stack: 2048 bytes Total Static RAM memory (data + bss): 10080 bytes Total RAM memory (data + bss + heap + stack): 53296 bytes Total Flash memory (text + data + misc): 79136 bytes

Thanks, Ajay.

posted by Ajay Kashyap 28 Feb 2017
7 years ago.

Hi,

I hope you don't mind me joining in. This topic is very relevant to what I'm trying to do. After checking out the latest mbed-os-example-ble/ I went to the BLE_Beacon example and added the magic Thread::attach_idle_hook(&sleep); as the first command of main(). Power consumption went down to below 1mA. Great!

Then I did the same change to BLE_Button but the result was that the LED stopped blinking (and power consumption is low). Apparently the EventQueue stopped working but BLE seems running fine. My real application is very similar to the BLE_Button example as it triggers sensor sampling from the EventQueue.

I'm trying to make low power work for a few days now without success, so some help would be greatly appreciated.

Many thanks,

-Tamás

HiTamás,

Although there is a tentative fix, which you can add in your code. This fix is still in testing stages, that Vincent was kind enough take the time to provide one and below is the link to a potential fix. It helped drop the power consumption drastically in our case. You will need to extensively test this fix as we are doing it currently.

https://github.com/ARMmbed/mbed-os/issues/3509

Thanks, Ajay

posted by Ajay Kashyap 02 Mar 2017