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

[Code with Explaniation] Power Management Example Code with Scheduler

Hi, I'm using the PWR_MGMT SDK 17.0.2 demo code on my nRF52 DK.

Q1) After enabling parameters in the sdk_config.h, the nRF52 DK could enter the Power Off Mode if I press the button manually (LED4 will turn on as the indication). However, by using Scheduler, nRF52 DK cannot automatically enter Power Off Mode after 5 seconds period. 

Here is the PuTTY information when Schedular timeout is triggerred: 

<info> pwr_mgmt: Shutdown request 0
<info> pwr_mgmt: Shutdown started. Type 0
<info> pwr_mgmt: SysOff handler 0x00003A85 => blocking

Here is my sdk_config.h

// <e> NRF_PWR_MGMT_ENABLED - nrf_pwr_mgmt - Power management module
//==========================================================
#ifndef NRF_PWR_MGMT_ENABLED
#define NRF_PWR_MGMT_ENABLED 1
#endif
// <e> NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED - Enables pin debug in the module.

// <i> Selected pin will be set when CPU is in sleep mode.
//==========================================================
// Set a GPIO (LED) on/off when the CPU is active/asleep, so that we could track of when the CPU is active or asleep. 
#ifndef NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED
#define NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED 1
#endif
// <o> NRF_PWR_MGMT_SLEEP_DEBUG_PIN  - Pin number
 
// <0=> 0 (P0.0) 
// <1=> 1 (P0.1) 
// <2=> 2 (P0.2) 
// <3=> 3 (P0.3) 
// <4=> 4 (P0.4) 
// <5=> 5 (P0.5) 
// <6=> 6 (P0.6) 
// <7=> 7 (P0.7) 
// <8=> 8 (P0.8) 
// <9=> 9 (P0.9) 
// <10=> 10 (P0.10) 
// <11=> 11 (P0.11) 
// <12=> 12 (P0.12) 
// <13=> 13 (P0.13) 
// <14=> 14 (P0.14) 
// <15=> 15 (P0.15) 
// <16=> 16 (P0.16) 
// <17=> 17 (P0.17) 
// <18=> 18 (P0.18) 
// <19=> 19 (P0.19) 
// <20=> 20 (P0.20) 
// <21=> 21 (P0.21) 
// <22=> 22 (P0.22) 
// <23=> 23 (P0.23) 
// <24=> 24 (P0.24) 
// <25=> 25 (P0.25) 
// <26=> 26 (P0.26) 
// <27=> 27 (P0.27) 
// <28=> 28 (P0.28) 
// <29=> 29 (P0.29) 
// <30=> 30 (P0.30) 
// <31=> 31 (P0.31) 
// <4294967295=> Not connected 

// We could select the debug pin = LED pin to debug (i.e. display) the sleep mode ON/OFF (For nRF52 DK, LED1/2/3/4 coresponds to P0.17/18/19/20)
// Since the LEDs on nRF52DK are active low, that means the LED will be turned ON when CPU is active. LED will be OFF when CPU is asleep. 
#ifndef NRF_PWR_MGMT_SLEEP_DEBUG_PIN
#define NRF_PWR_MGMT_SLEEP_DEBUG_PIN 20
#endif

// </e>

// <q> NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED  - Enables CPU usage monitor.
 

// <i> Module will trace percentage of CPU usage in one second intervals.
// This feature is used with the Logger Module to trace and log CPU usage (0% to 100%), and display through RTT/UART every 1 second. 
#ifndef NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED
#define NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED 1
#endif

// <e> NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED - Enable standby timeout.
//==========================================================
// Allow us to configure an auto-sleep after a period of inactivity (Note: requires the RTC to be configured)
#ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
#define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED 1
#endif
// <o> NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S - Standby timeout (in seconds). 
// <i> Shutdown procedure will begin no earlier than after this number of seconds.

#ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S
#define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S 5
#endif

// </e>

// <q> NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED  - Enables FPU event cleaning.
 
// To handle "sleep" or "exit sleep" when the Floating Point Unit (FPU) of the SoC is used
#ifndef NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED
#define NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED 1
#endif

// <q> NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY  - Blocked shutdown procedure will be retried every second.
 

#ifndef NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY
#define NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY 1
#endif

// <q> NRF_PWR_MGMT_CONFIG_USE_SCHEDULER  - Module will use @ref app_scheduler.
// 
#ifndef NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
#define NRF_PWR_MGMT_CONFIG_USE_SCHEDULER 1
#endif

// <o> NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT - The number of priorities for module handlers. 
// <i> The number of stages of the shutdown process.

#ifndef NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT
#define NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT 3
#endif

// </e>

// <q> NRF_SECTION_ITER_ENABLED  - nrf_section_iter - Section iterator
 

#ifndef NRF_SECTION_ITER_ENABLED
#define NRF_SECTION_ITER_ENABLED 1
#endif

Here is my main.c: 

#include <stdbool.h>
#include <stdint.h>
#include "boards.h"
#include "bsp.h"
#include "bsp_nfc.h"
#include "app_timer.h"
#include "nordic_common.h"
#include "app_error.h"
#include "nrf_drv_clock.h"
#include "nrf_pwr_mgmt.h"       // Power management library

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
#include "app_scheduler.h"
#define APP_SCHED_MAX_EVENT_SIZE    0   /**< Maximum size of scheduler events. */
#define APP_SCHED_QUEUE_SIZE        4   /**< Maximum number of events in the scheduler queue. */
#endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER

#define BTN_ID_READY                0   /**< ID of the button used to change the readiness to sleep. */
#define BTN_ID_SLEEP                1   /**< ID of the button used to put the application into sleep/system OFF mode. */
#define BTN_ID_WAKEUP               1   /**< ID of the button used to wake up the application. */
#define BTN_ID_RESET                2   /**< ID of the button used to reset the application. */

static volatile bool m_stay_in_sysoff;  /**< True if the application should stay in system OFF mode. */
static volatile bool m_is_ready;        /**< True if the application is ready to enter sleep/system OFF mode. */
static volatile bool m_sysoff_started;  /**< True if the application started sleep preparation. */














/**@brief Handler for shutdown preparation.
 */
bool shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
    uint32_t err_code;

    if (m_is_ready == false)
    {
        m_sysoff_started = true;
        return false;
    }

    switch (event)
    {
        case NRF_PWR_MGMT_EVT_PREPARE_SYSOFF:
            NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_SYSOFF");
            /**@brief       Function for disabling all buttons.
             *
             * @details     After calling this function, no buttons will generate events when pressed, and
             *              no buttons will be able to wake the system up from sleep mode.
             *
             * @retval      NRF_SUCCESS              If the buttons were successfully disabled.
             * @retval      NRF_ERROR_NOT_SUPPORTED  If the board has no buttons or BSP_SIMPLE is defined.
             * @return      A propagated error.
             */
            err_code = bsp_buttons_disable();
            APP_ERROR_CHECK(err_code);
            break;

        case NRF_PWR_MGMT_EVT_PREPARE_WAKEUP:
            NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_WAKEUP");
            err_code = bsp_buttons_disable();
            // Suppress NRF_ERROR_NOT_SUPPORTED return code.
            UNUSED_VARIABLE(err_code);

            /**@brief       Function for enabling wakeup from SYSTEM OFF for given button.
             *
             * @details     After calling this function, button can be used to wake up the chip.
             *              This function should only be called immediately before going into sleep.
             *
             * @param[in]   button_idx  Index of the button.
             *
             * @retval      NRF_SUCCESS              If the button was successfully enabled.
             * @retval      NRF_ERROR_NOT_SUPPORTED  If the board has no buttons or BSP_SIMPLE is defined.
             */
             err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
            // Suppress NRF_ERROR_NOT_SUPPORTED return code.
            UNUSED_VARIABLE(err_code);

            err_code = bsp_nfc_sleep_mode_prepare();
            // Suppress NRF_ERROR_NOT_SUPPORTED return code.
            UNUSED_VARIABLE(err_code);
            break;

        case NRF_PWR_MGMT_EVT_PREPARE_DFU:
            NRF_LOG_ERROR("Entering DFU is not supported by this example.");
            APP_ERROR_HANDLER(NRF_ERROR_API_NOT_IMPLEMENTED);
            break;

        case NRF_PWR_MGMT_EVT_PREPARE_RESET:
            NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_RESET");
            break;
    }

    err_code = app_timer_stop_all();
    APP_ERROR_CHECK(err_code);

    return true;
}









/**@brief Register application shutdown handler with priority 0. */
/**@brief   Macro for registering a shutdown handler to be called before entering system off sleep mode. 
 * It's very useful for doing application-level housekeeping before netering System off sleep mode. 
 * Modules that want to get events
 *          from this module must register the handler using this macro.
 *
 * @details This macro places the handler in a section named "pwr_mgmt_data".
 *
 * @param[in]   _handler    Event handler (@ref nrf_pwr_mgmt_shutdown_handler_t).
 * @param[in]   _priority   Priority of the given handler.
 */
NRF_PWR_MGMT_HANDLER_REGISTER(shutdown_handler, 0);










/**@brief Function for handling BSP events.
 */
static void bsp_evt_handler(bsp_event_t evt)
{
#if NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
    /*
    *@brief Function for indicating activity.
     *
     * @details Call this function whenever doing something that constitutes "activity".
     *          For example, whenever sending data, call this function to indicate that the application
     *          is active and should not disconnect any ongoing communication links.
     * This API is only applicable when NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED is enabled.
     * 
     * It is called to reset the counter and hence prevent the automatic sleep. 
     */ 
    nrf_pwr_mgmt_feed();
    NRF_LOG_INFO("bsp_evt_handler: Power management fed");
#endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED

    switch (evt)
    {
        // BSP_EVENT_KEY_0 = Default event of the push action of BSP_BUTTON_0 (only if this button is present)
        case BSP_EVENT_KEY_0:
            if (m_is_ready)
            {
                m_is_ready = false;
                NRF_LOG_INFO("System is not ready for shutdown");
            }
            else
            {
                m_is_ready = true;
                NRF_LOG_INFO("System is ready for shutdown");
            }
            if (m_sysoff_started && m_is_ready)
            {
                /**@brief Function for shutting down the system.
                 * @param[in] shutdown_type     Type of operation.
                 * @details All callbacks will be executed prior to shutdown.
                 */
                 nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_CONTINUE);
            }
            break;

        
        // BSP_EVENT_SYSOFF = The device should enter system off mode (without wakeup)
        case BSP_EVENT_SYSOFF:
            m_stay_in_sysoff = true;
            break;

        
        // BSP_EVENT_SLEEP = The device should enter sleep mode
        case BSP_EVENT_SLEEP:
            if (m_stay_in_sysoff)
            {
                nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_STAY_IN_SYSOFF);
            }
            else
            {
                nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
            }
            break;

        
        // BSP_EVENT_RESET = The device should reset
        case BSP_EVENT_RESET:
            nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_RESET);
            break;

        default:
            return; // no implementation needed
    }
}










/**@brief Function for initializing low-frequency clock.
    In this example, LFCLK is used for 
 */
static void lfclk_config(void)
{
    
    /**
     * @brief Function for initializing the nrf_drv_clock module.
     *
     * After initialization, the module is in power off state (clocks are not requested).
     *
     * @retval NRF_SUCCESS                          If the procedure was successful.
     * @retval NRF_ERROR_MODULE_ALREADY_INITIALIZED If the driver was already initialized.
     */
     ret_code_t err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);

    
    /**
     * @brief Function for requesting the LFCLK.
     *
     * The low-frequency clock can be requested by different modules
     * or contexts. The driver ensures that the clock will be started only when it is requested
     * the first time. If the clock is not ready but it was already started, the handler item that is
     * provided as an input parameter is added to the list of handlers that will be notified
     * when the clock is started. If the clock is already enabled, user callback is called from the
     * current context.
     *
     * The first request will start the selected LFCLK source. If an event handler is
     * provided, it will be called once the LFCLK is started. If the LFCLK was already started at this
     * time, the event handler will be called from the context of this function. Additionally,
     * the @ref nrf_drv_clock_lfclk_is_running function can be polled to check if the clock has started.
     *
     * @note When a SoftDevice is enabled, the LFCLK is always running and the driver cannot control it.
     *
     * @note The handler item provided by the user cannot be an automatic variable.
     *
     * @param[in] p_handler_item A pointer to the event handler structure.
     */
    nrf_drv_clock_lfclk_request(NULL);
}













/**
@brief Function for initializing the BSP module.
#define BTN_ID_READY                0   //< ID of the button used to change the readiness to sleep
#define BTN_ID_SLEEP                1   //< ID of the button used to put the application into sleep/system OFF mode
#define BTN_ID_WAKEUP               1   //< ID of the button used to wake up the application
#define BTN_ID_RESET                2   //< ID of the button used to reset the application
*/
static void bsp_configuration()
{
    uint32_t err_code;

    /**@brief       Function for initializing BSP.
     *
     * @details     The function initializes the board support package to allow state indication and
     *              button reaction. Default events are assigned to buttons.
     * @note        Before calling this function, you must initiate the following required modules:
     *              - @ref app_timer for LED support
     *              - @ref app_gpiote for button support
     *
     * @param[in]   type               Type of peripherals used.
     * @param[in]   callback           Function to be called when button press/event is detected.
     *
     * @retval      NRF_SUCCESS               If the BSP module was successfully initialized.
     * @retval      NRF_ERROR_INVALID_STATE   If the application timer module has not been initialized.
     * @retval      NRF_ERROR_NO_MEM          If the maximum number of timers has already been reached.
     * @retval      NRF_ERROR_INVALID_PARAM   If GPIOTE has too many users.
     * @retval      NRF_ERROR_INVALID_STATE   If button or GPIOTE has not been initialized.
     */
     err_code = bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler);
    APP_ERROR_CHECK(err_code);

    
    /**@brief       Function for assigning a specific event to a button.
     *
     * @details     This function allows redefinition of standard events assigned to buttons.
     *              To unassign events, provide the event @ref BSP_EVENT_NOTHING.
     *
     * @param[in]   button                   Button ID to be redefined.
     * @param[in]   action                   Button action to assign event to.
     * @param[in]   event                    Event to be assigned to button.
     *
     * @retval      NRF_SUCCESS              If the event was successfully assigned to button.
     * @retval      NRF_ERROR_INVALID_PARAM  If the button ID or button action was invalid.
     */
     // When press button 1 for 2000 ms (long press), the device should enter system off mode (without wakeup)
     err_code = bsp_event_to_button_action_assign(BTN_ID_SLEEP,
                                                 BSP_BUTTON_ACTION_LONG_PUSH,
                                                 BSP_EVENT_SYSOFF);
    APP_ERROR_CHECK(err_code);
    
    // When release button 1, The device should enter sleep mode.
    err_code = bsp_event_to_button_action_assign(BTN_ID_SLEEP,
                                                 BSP_BUTTON_ACTION_RELEASE,
                                                 BSP_EVENT_SLEEP);
    APP_ERROR_CHECK(err_code);
    
    //-----------------------------------------
    // When press button 0, assign this event to an action to prevent the action from generating an event (disable the action)
    err_code = bsp_event_to_button_action_assign(BTN_ID_READY,
                                                 BSP_BUTTON_ACTION_PUSH,
                                                 BSP_EVENT_NOTHING);
    APP_ERROR_CHECK(err_code);

    
    // When release button 0, Default event of the push action of BSP_BUTTON_0 (only if this button is present)
    err_code = bsp_event_to_button_action_assign(BTN_ID_READY,
                                                 BSP_BUTTON_ACTION_RELEASE,
                                                 BSP_EVENT_KEY_0);
    APP_ERROR_CHECK(err_code);
    
    //-----------------------------------------
    // When release button 2, The device should reset
    err_code = bsp_event_to_button_action_assign(BTN_ID_RESET,
                                                 BSP_BUTTON_ACTION_RELEASE,
                                                 BSP_EVENT_RESET);
    APP_ERROR_CHECK(err_code);
}

















/**
 * @brief Function for application main entry.
 */
int main(void)
{
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("Power Management example");

    // Cnfigure and initialize the LFCLK
    // We need a LFCLK for NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S feature
    // If use SoftDevice, this step is NOT needed because the SoftDevice already tuen on the LFCLK
    lfclk_config();
    uint32_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);


#if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    /**@brief Macro for initializing the event scheduler.
     *
     * @details It will also handle dimensioning and allocation of the memory buffer required by the
     *          scheduler, making sure the buffer is correctly aligned.
     *
     * @param[in] EVENT_SIZE   Maximum size of events to be passed through the scheduler.
     * @param[in] QUEUE_SIZE   Number of entries in scheduler queue (i.e. the maximum number of events
     *                         that can be scheduled for execution).
     *
     * @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
     *       several times as long as it is from the same location, e.g. to do a reinitialization).
     */
    APP_SCHED_INIT(APP_SCHED_MAX_EVENT_SIZE, APP_SCHED_QUEUE_SIZE);
#endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    
    
    bsp_configuration();

    // Initilize the power management library
    ret_code_t ret_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(ret_code);

    while (true)
    {
#if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
        app_sched_execute();
#endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER

        
        /**@brief Macro for processing a single log entry from a queue of deferred logs.
         *
         * You can call this macro from the main context or from the error handler to process
         * log entries one by one.
         *
         * @note If logs are not deferred, this call has no use and is defined as 'false'.
         *
         * @retval true    There are more logs to process in the buffer.
         * @retval false   No more logs in the buffer.
         * 
         * If there is no deferred log to print, enter system on sleep mode. 
         */
        if (NRF_LOG_PROCESS() == false)
        {
            // Function for running power management. Should run in the main loop.
            // Enter system on sleep mode
            // It makes sure that entering and existing sleep in safe and error-free whether SoftDevicce is used or not. 
            nrf_pwr_mgmt_run();
        }
    }
}

Q2) The PuTTY shows the message: 

<info> pwr_mgmt: Shutdown request 0
<info> pwr_mgmt: Shutdown started. Type 0
<info> pwr_mgmt: SysOff handler 0x00003A85 => blocking

What does 0x00003A85 and blocking mean? 

What does the 1st and 2nd 0 mean? I only found nrf_pwr_mgmt_shutdown_t shutdown_type and nrf_pwr_mgmt_evt_t   m_pwr_mgmt_evt has the following definition

Thanks in advance! 

Parents
  • Hi, I assume that you are running the peripheral/pwr_mgmt application in the SDK? It demonstrates how the application can automatically enter system OFF mode after the application is "ready", if the application is ready or not is given in the m_is_ready variable. If you just push button 2 on the DK, the application is not ready, and you will get the => blocking message, meaning that the application will enter system OFF mode as soon as this variable is set to true. The way to set this "ready" variable is to push button 1. So in order to enter system OFF, you have two options, press button 1 and wait for the timeout. Or press button 1, followed by button 2.

  • Thanks Stian! You're right! I understand the code now. To help other people to learn pwr_mgmt example code, I have added a lot of explanations to the sdk_config.h and main.c. Plz correct me if anything if wrong. 

    Here is main.c with explanations 

    #include <stdbool.h>
    #include <stdint.h>
    #include "boards.h"
    #include "bsp.h"
    #include "bsp_nfc.h"
    #include "app_timer.h"
    #include "nordic_common.h"
    #include "app_error.h"
    #include "nrf_drv_clock.h"
    #include "nrf_pwr_mgmt.h"       // Power management library
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    #include "app_scheduler.h"
    #define APP_SCHED_MAX_EVENT_SIZE    0   /**< Maximum size of scheduler events. */
    #define APP_SCHED_QUEUE_SIZE        4   /**< Maximum number of events in the scheduler queue. */
    #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    
    #define BTN_ID_READY                0   /**< ID of the button used to change the readiness to sleep. */
    #define BTN_ID_SLEEP                1   /**< ID of the button used to put the application into sleep/system OFF mode. */
    #define BTN_ID_WAKEUP               1   /**< ID of the button used to wake up the application. */
    #define BTN_ID_RESET                2   /**< ID of the button used to reset the application. */
    
    static volatile bool m_stay_in_sysoff;  /**< True if the application should stay in system OFF mode. */
    static volatile bool m_is_ready;        /**< True if the application is ready to enter sleep/system OFF mode. */
    static volatile bool m_sysoff_started;  /**< True if the application started sleep preparation. */
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**@brief Handler for shutdown preparation.
     * 
     * If the device is ready to enter to enter sleep/system OFF mode, return FALSE directly 
     * Otherwise, decide case by case
     *      1) If shuntdown type is "prepare to stay in System OFF state", disable all buttons 
     *      2) If shuntdown type is "prepare the wakeup mechanism", disable all buttons, and only enable the wake up button, and setup NFC tag as the wakeup source 
     *      3) If shuntdown type is "prepare to enter DFU mode.", print a meesage in hyperterminal. Handle the SDK error if exists: API not implemented
     *      4) If shuntdown type is "Application will prepare to chip reset", print a meesage in hyperterminal. 
     * Lastly, stop all timers. 
     */
    bool shutdown_handler(nrf_pwr_mgmt_evt_t event)
    {
        uint32_t err_code;
    
        if (m_is_ready == false)
        {
            m_sysoff_started = true;
            return false;
        }
    
        switch (event)
        {
            case NRF_PWR_MGMT_EVT_PREPARE_SYSOFF:
                NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_SYSOFF");
                /**@brief       Function for disabling all buttons.
                 *
                 * @details     After calling this function, no buttons will generate events when pressed, and
                 *              no buttons will be able to wake the system up from sleep mode.
                 *
                 * @retval      NRF_SUCCESS              If the buttons were successfully disabled.
                 * @retval      NRF_ERROR_NOT_SUPPORTED  If the board has no buttons or BSP_SIMPLE is defined.
                 * @return      A propagated error.
                 */
                err_code = bsp_buttons_disable();
                APP_ERROR_CHECK(err_code);
                break;
    
            case NRF_PWR_MGMT_EVT_PREPARE_WAKEUP:
                NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_WAKEUP");
                err_code = bsp_buttons_disable();
                // Suppress NRF_ERROR_NOT_SUPPORTED return code.
                UNUSED_VARIABLE(err_code);
    
                /**@brief       Function for enabling wakeup from SYSTEM OFF for given button.
                 *
                 * @details     After calling this function, button can be used to wake up the chip.
                 *              This function should only be called immediately before going into sleep.
                 *
                 * @param[in]   button_idx  Index of the button.
                 *
                 * @retval      NRF_SUCCESS              If the button was successfully enabled.
                 * @retval      NRF_ERROR_NOT_SUPPORTED  If the board has no buttons or BSP_SIMPLE is defined.
                 */
                 err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
                // Suppress NRF_ERROR_NOT_SUPPORTED return code.
                UNUSED_VARIABLE(err_code);
                
    
                /**@brief Function for setting up NFCT peripheral as wake-up source.
                 *
                 * This function must be called before going into System OFF mode.
                 *
                 * @note This function is only applicable if NFCT is used exclusively for wakeup.
                 *       If NFCT is used for a different purpose, this function cannot be used.
                 *
                 * @retval      NRF_SUCCESS     If NFCT peripheral was prepared successfully. Otherwise,
                 *                              a propagated error code is returned.
                 */
                err_code = bsp_nfc_sleep_mode_prepare();
                // Suppress NRF_ERROR_NOT_SUPPORTED return code.
                UNUSED_VARIABLE(err_code);
                break;
    
            case NRF_PWR_MGMT_EVT_PREPARE_DFU:
                NRF_LOG_ERROR("Entering DFU is not supported by this example.");
                // Handle the SDK errors: API not implemented
                APP_ERROR_HANDLER(NRF_ERROR_API_NOT_IMPLEMENTED);
                break;
    
            case NRF_PWR_MGMT_EVT_PREPARE_RESET:
                NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_RESET");
                break;
        }
        
    
        /**@brief Function for stopping all running timers.
         * @retval     NRF_SUCCESS               If all timers were successfully stopped.
         * @retval     NRF_ERROR_INVALID_STATE   If the application timer module has not been initialized.
         * @retval     NRF_ERROR_NO_MEM          If the timer operations queue was full.
         */
        err_code = app_timer_stop_all();
        APP_ERROR_CHECK(err_code);
    
        return true;
    }
    
    /**@brief Register application shutdown handler with priority 0. */
    /**@brief   Macro for registering a shutdown handler to be called before entering system off sleep mode. 
     * It's very useful for doing application-level housekeeping before netering System off sleep mode. 
     * Modules that want to get events
     *          from this module must register the handler using this macro.
     *
     * @details This macro places the handler in a section named "pwr_mgmt_data".
     *
     * @param[in]   _handler    Event handler (@ref nrf_pwr_mgmt_shutdown_handler_t).
     * @param[in]   _priority   Priority of the given handler.
     */
    NRF_PWR_MGMT_HANDLER_REGISTER(shutdown_handler, 0);
    
    
    
    
    
    
    
    
    
    
    /**@brief Function for handling BSP events.
     * 
     * If NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED feature is enabled, whenever we press any button, the countdown auto-turn-off timer will be reset
     * Then, based on which button is pressed, do action case by case: 
     * 
     *      1) When BSP_EVENT_KEY_0 event happens (i.e. button 0 is released), 
     *          - If device is at "ready to enter sleep/power off" state, change device state to "NOT ready to sleep/power off"
     *          - If deive is "NOT ready to sleep/power off", change state to "ready to sleep/power off"
     *         If device is "has started sleep preparation" AND "ready to sleep/power off", shutdown the device
     *
     *      2) When BSP_EVENT_SYSOFF event happens (i.e. button 1 is LONG pressed foe xxx ms), update device state to "stay in system OFF" mode (no other actions)
     *
     *      3) When BSP_EVENT_SLEEP event happens (i.e. button 1 is released), shutdown the device 
     * 
     *      4) When BSP_EVENT_RESET event happens (i.e. button 2 is released), reset the device 
     * 
     * 
     */
    static void bsp_evt_handler(bsp_event_t evt)
    {
    #if NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
        /*
        *@brief Function for indicating activity.
         *
         * @details Call this function whenever doing something that constitutes "activity".
         *          For example, whenever sending data, call this function to indicate that the application
         *          is active and should not disconnect any ongoing communication links.
         * This API is only applicable when NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED is enabled.
         * 
         * When we use NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED feature, it uses RTC1 hardware to countdown. 
         * nrf_pwr_mgmt_feed() is called to reset the counter and hence prevent the automatic sleep. 
         */ 
        nrf_pwr_mgmt_feed();
        NRF_LOG_INFO("bsp_evt_handler: Power management fed");
    #endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
    
        switch (evt)
        {
            // BSP_EVENT_KEY_0 = Default event of the push action of BSP_BUTTON_0 (only if this button is present)
            case BSP_EVENT_KEY_0:
                if (m_is_ready)
                {
                    m_is_ready = false;
                    NRF_LOG_INFO("System is not ready for shutdown");
                }
                else
                {
                    m_is_ready = true;
                    NRF_LOG_INFO("System is ready for shutdown");
                }
                if (m_sysoff_started && m_is_ready)
                {
                    /**@brief Function for shutting down the system.
                     * @param[in] shutdown_type     Type of operation.
                     * @details All callbacks will be executed prior to shutdown.
                     */
                     nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_CONTINUE);
                }
                break;
    
            
            // BSP_EVENT_SYSOFF = The device should enter system off mode (without wakeup)
            case BSP_EVENT_SYSOFF:
                m_stay_in_sysoff = true;
                break;
    
            
            // BSP_EVENT_SLEEP = The device should enter sleep mode
            case BSP_EVENT_SLEEP:
                if (m_stay_in_sysoff)
                {
                    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_STAY_IN_SYSOFF);
                }
                else
                {
                    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
                }
                break;
    
            
            // BSP_EVENT_RESET = The device should reset
            case BSP_EVENT_RESET:
                nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_RESET);
                break;
    
            default:
                return; // no implementation needed
        }
    }
    
    
    
    
    
    
    
    
    
    
    /**@brief Function for initializing low-frequency clock.
        In this example, LFCLK is used for 
     */
    static void lfclk_config(void)
    {
        
        /**
         * @brief Function for initializing the nrf_drv_clock module.
         *
         * After initialization, the module is in power off state (clocks are not requested).
         *
         * @retval NRF_SUCCESS                          If the procedure was successful.
         * @retval NRF_ERROR_MODULE_ALREADY_INITIALIZED If the driver was already initialized.
         */
         ret_code_t err_code = nrf_drv_clock_init();
        APP_ERROR_CHECK(err_code);
    
        
        /**
         * @brief Function for requesting the LFCLK.
         *
         * The low-frequency clock can be requested by different modules
         * or contexts. The driver ensures that the clock will be started only when it is requested
         * the first time. If the clock is not ready but it was already started, the handler item that is
         * provided as an input parameter is added to the list of handlers that will be notified
         * when the clock is started. If the clock is already enabled, user callback is called from the
         * current context.
         *
         * The first request will start the selected LFCLK source. If an event handler is
         * provided, it will be called once the LFCLK is started. If the LFCLK was already started at this
         * time, the event handler will be called from the context of this function. Additionally,
         * the @ref nrf_drv_clock_lfclk_is_running function can be polled to check if the clock has started.
         *
         * @note When a SoftDevice is enabled, the LFCLK is always running and the driver cannot control it.
         *
         * @note The handler item provided by the user cannot be an automatic variable.
         *
         * @param[in] p_handler_item A pointer to the event handler structure.
         */
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**
    Note: NOT all cases are used in this project
    
    @brief Function for initializing the BSP module.
    #define BTN_ID_READY                0   //< ID of the button used to change the readiness to sleep
    #define BTN_ID_SLEEP                1   //< ID of the button used to put the application into sleep/system OFF mode
    #define BTN_ID_WAKEUP               1   //< ID of the button used to wake up the application
    #define BTN_ID_RESET                2   //< ID of the button used to reset the application
    */
    static void bsp_configuration()
    {
        uint32_t err_code;
    
        /**@brief       Function for initializing BSP.
         *
         * @details     The function initializes the board support package to allow state indication and
         *              button reaction. Default events are assigned to buttons.
         * @note        Before calling this function, you must initiate the following required modules:
         *              - @ref app_timer for LED support
         *              - @ref app_gpiote for button support
         *
         * @param[in]   type               Type of peripherals used.
         * @param[in]   callback           Function to be called when button press/event is detected.
         *
         * @retval      NRF_SUCCESS               If the BSP module was successfully initialized.
         * @retval      NRF_ERROR_INVALID_STATE   If the application timer module has not been initialized.
         * @retval      NRF_ERROR_NO_MEM          If the maximum number of timers has already been reached.
         * @retval      NRF_ERROR_INVALID_PARAM   If GPIOTE has too many users.
         * @retval      NRF_ERROR_INVALID_STATE   If button or GPIOTE has not been initialized.
         */
         err_code = bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler);
        APP_ERROR_CHECK(err_code);
    
        
        /**@brief       Function for assigning a specific event to a button.
         *
         * @details     This function allows redefinition of standard events assigned to buttons.
         *              To unassign events, provide the event @ref BSP_EVENT_NOTHING.
         *
         * @param[in]   button                   Button ID to be redefined.
         * @param[in]   action                   Button action to assign event to.
         * @param[in]   event                    Event to be assigned to button.
         *
         * @retval      NRF_SUCCESS              If the event was successfully assigned to button.
         * @retval      NRF_ERROR_INVALID_PARAM  If the button ID or button action was invalid.
         */
         // When press button 1 for 2000 ms (long press), the device should enter system off mode (without wakeup)
         err_code = bsp_event_to_button_action_assign(BTN_ID_SLEEP,
                                                     BSP_BUTTON_ACTION_LONG_PUSH,
                                                     BSP_EVENT_SYSOFF);
        APP_ERROR_CHECK(err_code);
        
        // When release button 1, The device should enter sleep mode.
        err_code = bsp_event_to_button_action_assign(BTN_ID_SLEEP,
                                                     BSP_BUTTON_ACTION_RELEASE,
                                                     BSP_EVENT_SLEEP);
        APP_ERROR_CHECK(err_code);
        
        //-----------------------------------------
        // When press button 0, assign this event to an action to prevent the action from generating an event (disable the action)
        err_code = bsp_event_to_button_action_assign(BTN_ID_READY,
                                                     BSP_BUTTON_ACTION_PUSH,
                                                     BSP_EVENT_NOTHING);
        APP_ERROR_CHECK(err_code);
    
        
        // When release button 0, Default event of the push action of BSP_BUTTON_0 (only if this button is present)
        err_code = bsp_event_to_button_action_assign(BTN_ID_READY,
                                                     BSP_BUTTON_ACTION_RELEASE,
                                                     BSP_EVENT_KEY_0);
        APP_ERROR_CHECK(err_code);
        
        //-----------------------------------------
        // When release button 2, The device should reset
        err_code = bsp_event_to_button_action_assign(BTN_ID_RESET,
                                                     BSP_BUTTON_ACTION_RELEASE,
                                                     BSP_EVENT_RESET);
        APP_ERROR_CHECK(err_code);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("Power Management example");
    
        // Cnfigure and initialize the LFCLK
        // We need a LFCLK for NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S feature
        // If use SoftDevice, this step is NOT needed because the SoftDevice already tuen on the LFCLK
        lfclk_config();
        uint32_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
    
    #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
        /**@brief Macro for initializing the event scheduler.
         *
         * @details It will also handle dimensioning and allocation of the memory buffer required by the
         *          scheduler, making sure the buffer is correctly aligned.
         *
         * @param[in] EVENT_SIZE   Maximum size of events to be passed through the scheduler.
         * @param[in] QUEUE_SIZE   Number of entries in scheduler queue (i.e. the maximum number of events
         *                         that can be scheduled for execution).
         *
         * @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
         *       several times as long as it is from the same location, e.g. to do a reinitialization).
         */
        APP_SCHED_INIT(APP_SCHED_MAX_EVENT_SIZE, APP_SCHED_QUEUE_SIZE);
    #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
        
        
        bsp_configuration();
    
        // Initilize the power management library
        ret_code_t ret_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(ret_code);
    
        while (true)
        {
    #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
            /**@brief Function for executing all scheduled events.
             *
             * @details This function must be called from within the main loop. It will execute all events
             *          scheduled since the last time it was called.
             */
            app_sched_execute();
    #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    
            
            /**@brief Macro for processing a single log entry from a queue of deferred logs.
             *
             * You can call this macro from the main context or from the error handler to process
             * log entries one by one.
             *
             * @note If logs are not deferred, this call has no use and is defined as 'false'.
             *
             * @retval true    There are more logs to process in the buffer.
             * @retval false   No more logs in the buffer.
             * 
             * If there is no deferred log to print, enter system on sleep mode. 
             */
            if (NRF_LOG_PROCESS() == false)
            {
                // Function for running power management. Should run in the main loop.
                // Enter system on sleep mode
                // It makes sure that entering and existing sleep in safe and error-free whether SoftDevicce is used or not. 
                nrf_pwr_mgmt_run();
            }
        }
    }
    
    
    
    

    Here is my SDK explanations 

    // <e> NRF_PWR_MGMT_ENABLED - nrf_pwr_mgmt - Power management module
    //==========================================================
    #ifndef NRF_PWR_MGMT_ENABLED
    #define NRF_PWR_MGMT_ENABLED 1
    #endif
    // <e> NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED - Enables pin debug in the module.
    
    // <i> Selected pin will be set when CPU is in sleep mode.
    //==========================================================
    // Set a GPIO (LED) on/off when the CPU is active/asleep, so that we could track of when the CPU is active or asleep. 
    #ifndef NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED
    #define NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED 1
    #endif
    // <o> NRF_PWR_MGMT_SLEEP_DEBUG_PIN  - Pin number
     
    // <0=> 0 (P0.0) 
    // <1=> 1 (P0.1) 
    // <2=> 2 (P0.2) 
    // <3=> 3 (P0.3) 
    // <4=> 4 (P0.4) 
    // <5=> 5 (P0.5) 
    // <6=> 6 (P0.6) 
    // <7=> 7 (P0.7) 
    // <8=> 8 (P0.8) 
    // <9=> 9 (P0.9) 
    // <10=> 10 (P0.10) 
    // <11=> 11 (P0.11) 
    // <12=> 12 (P0.12) 
    // <13=> 13 (P0.13) 
    // <14=> 14 (P0.14) 
    // <15=> 15 (P0.15) 
    // <16=> 16 (P0.16) 
    // <17=> 17 (P0.17) 
    // <18=> 18 (P0.18) 
    // <19=> 19 (P0.19) 
    // <20=> 20 (P0.20) 
    // <21=> 21 (P0.21) 
    // <22=> 22 (P0.22) 
    // <23=> 23 (P0.23) 
    // <24=> 24 (P0.24) 
    // <25=> 25 (P0.25) 
    // <26=> 26 (P0.26) 
    // <27=> 27 (P0.27) 
    // <28=> 28 (P0.28) 
    // <29=> 29 (P0.29) 
    // <30=> 30 (P0.30) 
    // <31=> 31 (P0.31) 
    // <4294967295=> Not connected 
    
    // Check the nRF52 DK schematic diagram. 
    // We could select the debug pin = LED pin to debug (i.e. display) the sleep mode ON/OFF (For nRF52 DK, LED1/2/3/4 coresponds to P0.17/18/19/20)
    // Since the LEDs on nRF52DK are active low, that means the LED will be turned ON when CPU is active. LED will be OFF when CPU is asleep. 
    #ifndef NRF_PWR_MGMT_SLEEP_DEBUG_PIN
    #define NRF_PWR_MGMT_SLEEP_DEBUG_PIN 20
    #endif
    
    // </e>
    
    // <q> NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED  - Enables CPU usage monitor.
     
    
    // <i> Module will trace percentage of CPU usage in one second intervals.
    // This feature is used with the Logger Module to trace and log CPU usage (0% to 100%), and display through RTT/UART every 1 second. 
    #ifndef NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED
    #define NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED 1
    #endif
    
    // <e> NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED - Enable standby timeout.
    //==========================================================
    // Allow us to configure an auto-sleep after a period of inactivity (Note: requires the RTC1 hardware present on nRF5x to keep the timing)
    #ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
    #define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED 1
    #endif
    // <o> NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S - Standby timeout (in seconds). 
    // <i> Shutdown procedure will begin no earlier than after this number of seconds.
    
    #ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S
    #define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S 5
    #endif
    
    // </e>
    
    // <q> NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED  - Enables FPU event cleaning.
    // To handle "sleep" or "exit sleep" when the Floating Point Unit (FPU) of the SoC is used
    #ifndef NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED
    #define NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED 1
    #endif
    
    // <q> NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY  - Blocked shutdown procedure will be retried every second.
     
    
    #ifndef NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY
    #define NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY 0
    #endif
    
    // <q> NRF_PWR_MGMT_CONFIG_USE_SCHEDULER  - Module will use @ref app_scheduler.
    // 
    #ifndef NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    #define NRF_PWR_MGMT_CONFIG_USE_SCHEDULER 1
    #endif
    
    // <o> NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT - The number of priorities for module handlers. 
    // <i> The number of stages of the shutdown process.
    
    #ifndef NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT
    #define NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT 3
    #endif
    
    // </e>
    
    // <q> NRF_SECTION_ITER_ENABLED  - nrf_section_iter - Section iterator
     
    
    #ifndef NRF_SECTION_ITER_ENABLED
    #define NRF_SECTION_ITER_ENABLED 1
    #endif
    
    // <q> NRF_SORTLIST_ENABLED  - nrf_sortlist - Sorted list
     
    
    #ifndef NRF_SORTLIST_ENABLED
    #define NRF_SORTLIST_ENABLED 1
    #endif
    
    // <q> NRF_STRERROR_ENABLED  - nrf_strerror - Library for converting error code to string.
     
    
    #ifndef NRF_STRERROR_ENABLED
    #define NRF_STRERROR_ENABLED 1
    #endif
    
    

Reply
  • Thanks Stian! You're right! I understand the code now. To help other people to learn pwr_mgmt example code, I have added a lot of explanations to the sdk_config.h and main.c. Plz correct me if anything if wrong. 

    Here is main.c with explanations 

    #include <stdbool.h>
    #include <stdint.h>
    #include "boards.h"
    #include "bsp.h"
    #include "bsp_nfc.h"
    #include "app_timer.h"
    #include "nordic_common.h"
    #include "app_error.h"
    #include "nrf_drv_clock.h"
    #include "nrf_pwr_mgmt.h"       // Power management library
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    #include "app_scheduler.h"
    #define APP_SCHED_MAX_EVENT_SIZE    0   /**< Maximum size of scheduler events. */
    #define APP_SCHED_QUEUE_SIZE        4   /**< Maximum number of events in the scheduler queue. */
    #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    
    #define BTN_ID_READY                0   /**< ID of the button used to change the readiness to sleep. */
    #define BTN_ID_SLEEP                1   /**< ID of the button used to put the application into sleep/system OFF mode. */
    #define BTN_ID_WAKEUP               1   /**< ID of the button used to wake up the application. */
    #define BTN_ID_RESET                2   /**< ID of the button used to reset the application. */
    
    static volatile bool m_stay_in_sysoff;  /**< True if the application should stay in system OFF mode. */
    static volatile bool m_is_ready;        /**< True if the application is ready to enter sleep/system OFF mode. */
    static volatile bool m_sysoff_started;  /**< True if the application started sleep preparation. */
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**@brief Handler for shutdown preparation.
     * 
     * If the device is ready to enter to enter sleep/system OFF mode, return FALSE directly 
     * Otherwise, decide case by case
     *      1) If shuntdown type is "prepare to stay in System OFF state", disable all buttons 
     *      2) If shuntdown type is "prepare the wakeup mechanism", disable all buttons, and only enable the wake up button, and setup NFC tag as the wakeup source 
     *      3) If shuntdown type is "prepare to enter DFU mode.", print a meesage in hyperterminal. Handle the SDK error if exists: API not implemented
     *      4) If shuntdown type is "Application will prepare to chip reset", print a meesage in hyperterminal. 
     * Lastly, stop all timers. 
     */
    bool shutdown_handler(nrf_pwr_mgmt_evt_t event)
    {
        uint32_t err_code;
    
        if (m_is_ready == false)
        {
            m_sysoff_started = true;
            return false;
        }
    
        switch (event)
        {
            case NRF_PWR_MGMT_EVT_PREPARE_SYSOFF:
                NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_SYSOFF");
                /**@brief       Function for disabling all buttons.
                 *
                 * @details     After calling this function, no buttons will generate events when pressed, and
                 *              no buttons will be able to wake the system up from sleep mode.
                 *
                 * @retval      NRF_SUCCESS              If the buttons were successfully disabled.
                 * @retval      NRF_ERROR_NOT_SUPPORTED  If the board has no buttons or BSP_SIMPLE is defined.
                 * @return      A propagated error.
                 */
                err_code = bsp_buttons_disable();
                APP_ERROR_CHECK(err_code);
                break;
    
            case NRF_PWR_MGMT_EVT_PREPARE_WAKEUP:
                NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_WAKEUP");
                err_code = bsp_buttons_disable();
                // Suppress NRF_ERROR_NOT_SUPPORTED return code.
                UNUSED_VARIABLE(err_code);
    
                /**@brief       Function for enabling wakeup from SYSTEM OFF for given button.
                 *
                 * @details     After calling this function, button can be used to wake up the chip.
                 *              This function should only be called immediately before going into sleep.
                 *
                 * @param[in]   button_idx  Index of the button.
                 *
                 * @retval      NRF_SUCCESS              If the button was successfully enabled.
                 * @retval      NRF_ERROR_NOT_SUPPORTED  If the board has no buttons or BSP_SIMPLE is defined.
                 */
                 err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
                // Suppress NRF_ERROR_NOT_SUPPORTED return code.
                UNUSED_VARIABLE(err_code);
                
    
                /**@brief Function for setting up NFCT peripheral as wake-up source.
                 *
                 * This function must be called before going into System OFF mode.
                 *
                 * @note This function is only applicable if NFCT is used exclusively for wakeup.
                 *       If NFCT is used for a different purpose, this function cannot be used.
                 *
                 * @retval      NRF_SUCCESS     If NFCT peripheral was prepared successfully. Otherwise,
                 *                              a propagated error code is returned.
                 */
                err_code = bsp_nfc_sleep_mode_prepare();
                // Suppress NRF_ERROR_NOT_SUPPORTED return code.
                UNUSED_VARIABLE(err_code);
                break;
    
            case NRF_PWR_MGMT_EVT_PREPARE_DFU:
                NRF_LOG_ERROR("Entering DFU is not supported by this example.");
                // Handle the SDK errors: API not implemented
                APP_ERROR_HANDLER(NRF_ERROR_API_NOT_IMPLEMENTED);
                break;
    
            case NRF_PWR_MGMT_EVT_PREPARE_RESET:
                NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_RESET");
                break;
        }
        
    
        /**@brief Function for stopping all running timers.
         * @retval     NRF_SUCCESS               If all timers were successfully stopped.
         * @retval     NRF_ERROR_INVALID_STATE   If the application timer module has not been initialized.
         * @retval     NRF_ERROR_NO_MEM          If the timer operations queue was full.
         */
        err_code = app_timer_stop_all();
        APP_ERROR_CHECK(err_code);
    
        return true;
    }
    
    /**@brief Register application shutdown handler with priority 0. */
    /**@brief   Macro for registering a shutdown handler to be called before entering system off sleep mode. 
     * It's very useful for doing application-level housekeeping before netering System off sleep mode. 
     * Modules that want to get events
     *          from this module must register the handler using this macro.
     *
     * @details This macro places the handler in a section named "pwr_mgmt_data".
     *
     * @param[in]   _handler    Event handler (@ref nrf_pwr_mgmt_shutdown_handler_t).
     * @param[in]   _priority   Priority of the given handler.
     */
    NRF_PWR_MGMT_HANDLER_REGISTER(shutdown_handler, 0);
    
    
    
    
    
    
    
    
    
    
    /**@brief Function for handling BSP events.
     * 
     * If NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED feature is enabled, whenever we press any button, the countdown auto-turn-off timer will be reset
     * Then, based on which button is pressed, do action case by case: 
     * 
     *      1) When BSP_EVENT_KEY_0 event happens (i.e. button 0 is released), 
     *          - If device is at "ready to enter sleep/power off" state, change device state to "NOT ready to sleep/power off"
     *          - If deive is "NOT ready to sleep/power off", change state to "ready to sleep/power off"
     *         If device is "has started sleep preparation" AND "ready to sleep/power off", shutdown the device
     *
     *      2) When BSP_EVENT_SYSOFF event happens (i.e. button 1 is LONG pressed foe xxx ms), update device state to "stay in system OFF" mode (no other actions)
     *
     *      3) When BSP_EVENT_SLEEP event happens (i.e. button 1 is released), shutdown the device 
     * 
     *      4) When BSP_EVENT_RESET event happens (i.e. button 2 is released), reset the device 
     * 
     * 
     */
    static void bsp_evt_handler(bsp_event_t evt)
    {
    #if NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
        /*
        *@brief Function for indicating activity.
         *
         * @details Call this function whenever doing something that constitutes "activity".
         *          For example, whenever sending data, call this function to indicate that the application
         *          is active and should not disconnect any ongoing communication links.
         * This API is only applicable when NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED is enabled.
         * 
         * When we use NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED feature, it uses RTC1 hardware to countdown. 
         * nrf_pwr_mgmt_feed() is called to reset the counter and hence prevent the automatic sleep. 
         */ 
        nrf_pwr_mgmt_feed();
        NRF_LOG_INFO("bsp_evt_handler: Power management fed");
    #endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
    
        switch (evt)
        {
            // BSP_EVENT_KEY_0 = Default event of the push action of BSP_BUTTON_0 (only if this button is present)
            case BSP_EVENT_KEY_0:
                if (m_is_ready)
                {
                    m_is_ready = false;
                    NRF_LOG_INFO("System is not ready for shutdown");
                }
                else
                {
                    m_is_ready = true;
                    NRF_LOG_INFO("System is ready for shutdown");
                }
                if (m_sysoff_started && m_is_ready)
                {
                    /**@brief Function for shutting down the system.
                     * @param[in] shutdown_type     Type of operation.
                     * @details All callbacks will be executed prior to shutdown.
                     */
                     nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_CONTINUE);
                }
                break;
    
            
            // BSP_EVENT_SYSOFF = The device should enter system off mode (without wakeup)
            case BSP_EVENT_SYSOFF:
                m_stay_in_sysoff = true;
                break;
    
            
            // BSP_EVENT_SLEEP = The device should enter sleep mode
            case BSP_EVENT_SLEEP:
                if (m_stay_in_sysoff)
                {
                    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_STAY_IN_SYSOFF);
                }
                else
                {
                    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
                }
                break;
    
            
            // BSP_EVENT_RESET = The device should reset
            case BSP_EVENT_RESET:
                nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_RESET);
                break;
    
            default:
                return; // no implementation needed
        }
    }
    
    
    
    
    
    
    
    
    
    
    /**@brief Function for initializing low-frequency clock.
        In this example, LFCLK is used for 
     */
    static void lfclk_config(void)
    {
        
        /**
         * @brief Function for initializing the nrf_drv_clock module.
         *
         * After initialization, the module is in power off state (clocks are not requested).
         *
         * @retval NRF_SUCCESS                          If the procedure was successful.
         * @retval NRF_ERROR_MODULE_ALREADY_INITIALIZED If the driver was already initialized.
         */
         ret_code_t err_code = nrf_drv_clock_init();
        APP_ERROR_CHECK(err_code);
    
        
        /**
         * @brief Function for requesting the LFCLK.
         *
         * The low-frequency clock can be requested by different modules
         * or contexts. The driver ensures that the clock will be started only when it is requested
         * the first time. If the clock is not ready but it was already started, the handler item that is
         * provided as an input parameter is added to the list of handlers that will be notified
         * when the clock is started. If the clock is already enabled, user callback is called from the
         * current context.
         *
         * The first request will start the selected LFCLK source. If an event handler is
         * provided, it will be called once the LFCLK is started. If the LFCLK was already started at this
         * time, the event handler will be called from the context of this function. Additionally,
         * the @ref nrf_drv_clock_lfclk_is_running function can be polled to check if the clock has started.
         *
         * @note When a SoftDevice is enabled, the LFCLK is always running and the driver cannot control it.
         *
         * @note The handler item provided by the user cannot be an automatic variable.
         *
         * @param[in] p_handler_item A pointer to the event handler structure.
         */
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**
    Note: NOT all cases are used in this project
    
    @brief Function for initializing the BSP module.
    #define BTN_ID_READY                0   //< ID of the button used to change the readiness to sleep
    #define BTN_ID_SLEEP                1   //< ID of the button used to put the application into sleep/system OFF mode
    #define BTN_ID_WAKEUP               1   //< ID of the button used to wake up the application
    #define BTN_ID_RESET                2   //< ID of the button used to reset the application
    */
    static void bsp_configuration()
    {
        uint32_t err_code;
    
        /**@brief       Function for initializing BSP.
         *
         * @details     The function initializes the board support package to allow state indication and
         *              button reaction. Default events are assigned to buttons.
         * @note        Before calling this function, you must initiate the following required modules:
         *              - @ref app_timer for LED support
         *              - @ref app_gpiote for button support
         *
         * @param[in]   type               Type of peripherals used.
         * @param[in]   callback           Function to be called when button press/event is detected.
         *
         * @retval      NRF_SUCCESS               If the BSP module was successfully initialized.
         * @retval      NRF_ERROR_INVALID_STATE   If the application timer module has not been initialized.
         * @retval      NRF_ERROR_NO_MEM          If the maximum number of timers has already been reached.
         * @retval      NRF_ERROR_INVALID_PARAM   If GPIOTE has too many users.
         * @retval      NRF_ERROR_INVALID_STATE   If button or GPIOTE has not been initialized.
         */
         err_code = bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler);
        APP_ERROR_CHECK(err_code);
    
        
        /**@brief       Function for assigning a specific event to a button.
         *
         * @details     This function allows redefinition of standard events assigned to buttons.
         *              To unassign events, provide the event @ref BSP_EVENT_NOTHING.
         *
         * @param[in]   button                   Button ID to be redefined.
         * @param[in]   action                   Button action to assign event to.
         * @param[in]   event                    Event to be assigned to button.
         *
         * @retval      NRF_SUCCESS              If the event was successfully assigned to button.
         * @retval      NRF_ERROR_INVALID_PARAM  If the button ID or button action was invalid.
         */
         // When press button 1 for 2000 ms (long press), the device should enter system off mode (without wakeup)
         err_code = bsp_event_to_button_action_assign(BTN_ID_SLEEP,
                                                     BSP_BUTTON_ACTION_LONG_PUSH,
                                                     BSP_EVENT_SYSOFF);
        APP_ERROR_CHECK(err_code);
        
        // When release button 1, The device should enter sleep mode.
        err_code = bsp_event_to_button_action_assign(BTN_ID_SLEEP,
                                                     BSP_BUTTON_ACTION_RELEASE,
                                                     BSP_EVENT_SLEEP);
        APP_ERROR_CHECK(err_code);
        
        //-----------------------------------------
        // When press button 0, assign this event to an action to prevent the action from generating an event (disable the action)
        err_code = bsp_event_to_button_action_assign(BTN_ID_READY,
                                                     BSP_BUTTON_ACTION_PUSH,
                                                     BSP_EVENT_NOTHING);
        APP_ERROR_CHECK(err_code);
    
        
        // When release button 0, Default event of the push action of BSP_BUTTON_0 (only if this button is present)
        err_code = bsp_event_to_button_action_assign(BTN_ID_READY,
                                                     BSP_BUTTON_ACTION_RELEASE,
                                                     BSP_EVENT_KEY_0);
        APP_ERROR_CHECK(err_code);
        
        //-----------------------------------------
        // When release button 2, The device should reset
        err_code = bsp_event_to_button_action_assign(BTN_ID_RESET,
                                                     BSP_BUTTON_ACTION_RELEASE,
                                                     BSP_EVENT_RESET);
        APP_ERROR_CHECK(err_code);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("Power Management example");
    
        // Cnfigure and initialize the LFCLK
        // We need a LFCLK for NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S feature
        // If use SoftDevice, this step is NOT needed because the SoftDevice already tuen on the LFCLK
        lfclk_config();
        uint32_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
    
    #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
        /**@brief Macro for initializing the event scheduler.
         *
         * @details It will also handle dimensioning and allocation of the memory buffer required by the
         *          scheduler, making sure the buffer is correctly aligned.
         *
         * @param[in] EVENT_SIZE   Maximum size of events to be passed through the scheduler.
         * @param[in] QUEUE_SIZE   Number of entries in scheduler queue (i.e. the maximum number of events
         *                         that can be scheduled for execution).
         *
         * @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
         *       several times as long as it is from the same location, e.g. to do a reinitialization).
         */
        APP_SCHED_INIT(APP_SCHED_MAX_EVENT_SIZE, APP_SCHED_QUEUE_SIZE);
    #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
        
        
        bsp_configuration();
    
        // Initilize the power management library
        ret_code_t ret_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(ret_code);
    
        while (true)
        {
    #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
            /**@brief Function for executing all scheduled events.
             *
             * @details This function must be called from within the main loop. It will execute all events
             *          scheduled since the last time it was called.
             */
            app_sched_execute();
    #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    
            
            /**@brief Macro for processing a single log entry from a queue of deferred logs.
             *
             * You can call this macro from the main context or from the error handler to process
             * log entries one by one.
             *
             * @note If logs are not deferred, this call has no use and is defined as 'false'.
             *
             * @retval true    There are more logs to process in the buffer.
             * @retval false   No more logs in the buffer.
             * 
             * If there is no deferred log to print, enter system on sleep mode. 
             */
            if (NRF_LOG_PROCESS() == false)
            {
                // Function for running power management. Should run in the main loop.
                // Enter system on sleep mode
                // It makes sure that entering and existing sleep in safe and error-free whether SoftDevicce is used or not. 
                nrf_pwr_mgmt_run();
            }
        }
    }
    
    
    
    

    Here is my SDK explanations 

    // <e> NRF_PWR_MGMT_ENABLED - nrf_pwr_mgmt - Power management module
    //==========================================================
    #ifndef NRF_PWR_MGMT_ENABLED
    #define NRF_PWR_MGMT_ENABLED 1
    #endif
    // <e> NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED - Enables pin debug in the module.
    
    // <i> Selected pin will be set when CPU is in sleep mode.
    //==========================================================
    // Set a GPIO (LED) on/off when the CPU is active/asleep, so that we could track of when the CPU is active or asleep. 
    #ifndef NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED
    #define NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED 1
    #endif
    // <o> NRF_PWR_MGMT_SLEEP_DEBUG_PIN  - Pin number
     
    // <0=> 0 (P0.0) 
    // <1=> 1 (P0.1) 
    // <2=> 2 (P0.2) 
    // <3=> 3 (P0.3) 
    // <4=> 4 (P0.4) 
    // <5=> 5 (P0.5) 
    // <6=> 6 (P0.6) 
    // <7=> 7 (P0.7) 
    // <8=> 8 (P0.8) 
    // <9=> 9 (P0.9) 
    // <10=> 10 (P0.10) 
    // <11=> 11 (P0.11) 
    // <12=> 12 (P0.12) 
    // <13=> 13 (P0.13) 
    // <14=> 14 (P0.14) 
    // <15=> 15 (P0.15) 
    // <16=> 16 (P0.16) 
    // <17=> 17 (P0.17) 
    // <18=> 18 (P0.18) 
    // <19=> 19 (P0.19) 
    // <20=> 20 (P0.20) 
    // <21=> 21 (P0.21) 
    // <22=> 22 (P0.22) 
    // <23=> 23 (P0.23) 
    // <24=> 24 (P0.24) 
    // <25=> 25 (P0.25) 
    // <26=> 26 (P0.26) 
    // <27=> 27 (P0.27) 
    // <28=> 28 (P0.28) 
    // <29=> 29 (P0.29) 
    // <30=> 30 (P0.30) 
    // <31=> 31 (P0.31) 
    // <4294967295=> Not connected 
    
    // Check the nRF52 DK schematic diagram. 
    // We could select the debug pin = LED pin to debug (i.e. display) the sleep mode ON/OFF (For nRF52 DK, LED1/2/3/4 coresponds to P0.17/18/19/20)
    // Since the LEDs on nRF52DK are active low, that means the LED will be turned ON when CPU is active. LED will be OFF when CPU is asleep. 
    #ifndef NRF_PWR_MGMT_SLEEP_DEBUG_PIN
    #define NRF_PWR_MGMT_SLEEP_DEBUG_PIN 20
    #endif
    
    // </e>
    
    // <q> NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED  - Enables CPU usage monitor.
     
    
    // <i> Module will trace percentage of CPU usage in one second intervals.
    // This feature is used with the Logger Module to trace and log CPU usage (0% to 100%), and display through RTT/UART every 1 second. 
    #ifndef NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED
    #define NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED 1
    #endif
    
    // <e> NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED - Enable standby timeout.
    //==========================================================
    // Allow us to configure an auto-sleep after a period of inactivity (Note: requires the RTC1 hardware present on nRF5x to keep the timing)
    #ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
    #define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED 1
    #endif
    // <o> NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S - Standby timeout (in seconds). 
    // <i> Shutdown procedure will begin no earlier than after this number of seconds.
    
    #ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S
    #define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S 5
    #endif
    
    // </e>
    
    // <q> NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED  - Enables FPU event cleaning.
    // To handle "sleep" or "exit sleep" when the Floating Point Unit (FPU) of the SoC is used
    #ifndef NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED
    #define NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED 1
    #endif
    
    // <q> NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY  - Blocked shutdown procedure will be retried every second.
     
    
    #ifndef NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY
    #define NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY 0
    #endif
    
    // <q> NRF_PWR_MGMT_CONFIG_USE_SCHEDULER  - Module will use @ref app_scheduler.
    // 
    #ifndef NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    #define NRF_PWR_MGMT_CONFIG_USE_SCHEDULER 1
    #endif
    
    // <o> NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT - The number of priorities for module handlers. 
    // <i> The number of stages of the shutdown process.
    
    #ifndef NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT
    #define NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT 3
    #endif
    
    // </e>
    
    // <q> NRF_SECTION_ITER_ENABLED  - nrf_section_iter - Section iterator
     
    
    #ifndef NRF_SECTION_ITER_ENABLED
    #define NRF_SECTION_ITER_ENABLED 1
    #endif
    
    // <q> NRF_SORTLIST_ENABLED  - nrf_sortlist - Sorted list
     
    
    #ifndef NRF_SORTLIST_ENABLED
    #define NRF_SORTLIST_ENABLED 1
    #endif
    
    // <q> NRF_STRERROR_ENABLED  - nrf_strerror - Library for converting error code to string.
     
    
    #ifndef NRF_STRERROR_ENABLED
    #define NRF_STRERROR_ENABLED 1
    #endif
    
    

Children
No Data
Related