11. Sleep Mode

Before we start working with sleep mode, we will take a look at the power consumption of our implementation. The Power Profiler tool of the SmartSnippets™ Toolbox will allow us to observe how power is consumed in real time.

Power Profiler

The Power Profiler is available on the PRO development kits, and you can see how to use it here. The DA1453x USB kit and the DA14585/6 BASIC kits do not support the Toolbox Power Profiler.

Running our firmware at its current state should expose an advertisement event looking something like this :
_images/ss_initial.png

Figure 16 Power consumption of our current implementation (DA14531 in buck mode).You can stop the Power Profiler and use the scroll wheel of your mouse to zoom in on a single event

What we observe here is an idle current of approximately 0.25mA. We can also see an advertisement on three channels, each consisting of one TX pulse (approximately 3.5mA) and one RX pulse (approximately 2.2mA).

Why enabling Sleep mode?

Between advertising events, the device really isn’t doing anything other than burning power, so we should enable sleep mode and thereby save a lot of power.

11.1. Enabling Sleep Mode

As mentioned in the introduction, sleep mode functionality is fully managed by the SDK.

All we have to do to enable this functionality is to change the default sleep mode in user_config.h from:

/******************************************
* Default sleep mode. Possible values are:
*
* - ARCH_SLEEP_OFF
* - ARCH_EXT_SLEEP_ON
* - ARCH_EXT_SLEEP_OTP_COPY_ON
*
******************************************
*/

- static const sleep_state_t app_default_sleep_mode = ARCH_SLEEP_OFF;
+ static const sleep_state_t app_default_sleep_mode = ARCH_EXT_SLEEP_ON;
If we build the project and run it again (F7 then twice Ctrl+F5), we will see the following in the Power Profiler:
_images/ss_sleep.png

Figure 17 Our implementation with sleep enabled (DA14531 in buck mode)

Warning

The Chip wont go to sleep mode with the debugger attached.

The current has been reduced dramatically between the advertising events. In fact, the current has dropped from the 0.25mA we observed previously to as little as 1.2uA. We can also see that the device knows when to wake up for the next advertising event, and this is all completely handled by the SDK6. All we did was enabling sleep.

Using the DA14531 or DA1453x DevkitP, you may have noticed that you have to physically reset target to attach the debugger after you enabled sleep. You can get around this by lowering the J-Link clock speed to 2MHz. Open the Debug tab in Options for Target (Alt + F7) and change the Max Clock setting

as shown below:
_images/531_clock_2mhz.png

Figure 18 Change the J-Link clock setting when using the DA14531 PRO kit

Hint

You can find a lot more information about sleep mode in the DA1453x Sleep Mode Tutorial.

11.2. Waking up From Sleep

When the device wakes back up from sleep, it attempts to re-initialize the GPIOs and other peripherals. This is achieved by calling the periph_init() function in user_periph_setup.c right after waking up. For digital output pins, this presents an issue because the re-configuration of the output pin also sets the output state.

If you establish a BLE connection with our device, you might notice an issue. The LED, initially programmed to stay on for 2 seconds, now only briefly flashes after enabling sleep.

By using the Power Profiler, you can observe the expected behavior:
_images/ss_led-off.png

Figure 19 The LED turns off when the device wakes up from sleep (DA14531 in buck mode)

The LED turns on when the connection is established but turns off as soon as we wake up for the next connection event.

In the following we will fix this issue, by retaining the desired state of the LED in a variable and configure the digital output depending on the state of this variable.
  1. First we will declare a boolean in such a way that it will be retained during sleep. In user_peripheral_template.c, right after the include statements at the top, declare a retained global boolean as shown below:

    bool my_led_state __SECTION_ZERO("retention_mem_area0"); // @RETENTION MEMORY
    
  2. We will then re-route the app_on_init callback to user space. This will provide us a place where we can initialize the my_led_state boolean. In user_callback_config.h locate the code snippet below:

    static const struct arch_main_loop_callbacks user_app_main_loop_callbacks = {
    -   .app_on_init         = default_app_on_init,
    +   .app_on_init         = user_app_on_init,
       ~
    
  3. Now, add a prototype to this function in user_peripheral_template.h:

    void user_app_on_init(void);
    
  4. This gives us a function that we can use for initialization of our global variable. In user_peripheral_template.c add the following code:

    void user_app_on_init(void)
    {
       my_led_state = false;
       default_app_on_init();
    }
    

    We have implemented control of the LED in user_empty_peripheral_template.c.We must now make sure that our global boolean, my_led_state, is set accordingly.

  5. In the control_LED() function, as a last statement, add the following:

    my_led_state = state;
    
    • At this point we can rely on my_led_state to retain the desired LED state during sleep.

  6. In set_pad_functions() of user_periph_setup.c, replace the GPIO configuration of our LED pin with the following:

    extern bool my_led_state;
    GPIO_ConfigurePin(LED_PORT, LED_PIN, OUTPUT, PID_GPIO, my_led_state);
    
  7. Build the project and run it again to ensure that the LED remains on for 2 seconds after a BLE connection is established.