nRF52810 will not enter deep sleep (system level power management)

nRF52810 will not enter deep sleep (system level power management). The method used is through pm_policy_next_state, sleep entry function , sleep exit function, and  PM notifier. All peripherals are suspended/disabled to ensure no blocking. These are in prj.conf:

CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_POLICY_CUSTOM=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_DEVICE_SYSTEM_MANAGED=y
This is the source code: 
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/pm/pm.h>
#include <zephyr/sys/printk.h>
#include <hal/nrf_gpio.h>
#include <hal/nrf_clock.h>
#include <cmsis_core.h>
#include <hal/nrf_power.h>

#include <hal/nrf_gpiote.h>


#include <nrfx.h>
#include <nrfx_gpiote.h>

// Replace with your actual button pin
#define WAKEUP_PIN 20  // Example: P0.20

// --- Power diagnostics ---
static void print_clock_state(void)
{
    bool hfclk = nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_HFCLK);
    bool lfclk = nrf_clock_lf_is_running(NRF_CLOCK);
    printk("HFCLK running: %d\n", hfclk);
    printk("LFCLK running: %d\n", lfclk);
}

static void check_pending_irqs(void)
{
    printk("Checking pending IRQs...\n");
    for (int i = 0; i < 32; i++) {
        if (NVIC_GetPendingIRQ(i)) {
            printk("IRQ %d is pending\n", i);
        }
    }
}

static void check_gpio_sense_config(void)
{
    printk("Checking GPIO sense configs...\n");
    for (int pin = 0; pin < 32; pin++) {
        nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
        if (sense != NRF_GPIO_PIN_NOSENSE) {
            printk("Pin P0.%d has sense enabled (%s)\n",
                   pin,
                   sense == NRF_GPIO_PIN_SENSE_HIGH ? "HIGH" : "LOW");
        }
    }
}

const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
{
    static const struct pm_state_info suspend_state = {
        .state = PM_STATE_SUSPEND_TO_RAM,
        .substate_id = 0,
        .min_residency_us = 1000,
        .exit_latency_us = 500,
    };
    printk("pm_policy_next_state() called\n");
    // Replace with your custom condition
    bool ready_to_sleep = true;
    if (ready_to_sleep) {
        return &suspend_state;
    }

    return NULL; // Stay in active state
}

void pm_state_set(enum pm_state state, uint8_t substate_id)
{
    if (state == PM_STATE_SUSPEND_TO_RAM) {
        printk("PM: Preparing to suspend...\n");

        // Configure wake-up GPIO
        nrf_gpio_cfg_input(WAKEUP_PIN, NRF_GPIO_PIN_PULLUP);
        nrf_gpio_cfg_sense_set(WAKEUP_PIN, NRF_GPIO_PIN_SENSE_LOW);

        print_clock_state();
        check_pending_irqs();
        check_gpio_sense_config();

        __SEV();
        __WFE();
        __WFE();

        printk("PM: Woke from suspend\n");
    }
}

void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
{
    // Optional: re-init post wake if needed
}

static void on_pm_entry(enum pm_state state)
{
    if (state == PM_STATE_SUSPEND_TO_RAM) {
        //suspend_peripherals();
        printk("Notifier: system preparing to suspend.\n");
    }
}

static void on_pm_exit(enum pm_state state)
{
    if (state == PM_STATE_SUSPEND_TO_RAM) {
        //resume_peripherals();
        printk("Notifier: system resumed from suspend.\n");
    }
}

static struct pm_notifier pm_cb = {
    .state_entry = on_pm_entry,
    .state_exit = on_pm_exit,
};

static int pm_hooks_init(void)
{
    pm_notifier_register(&pm_cb);
    return 0;
}

SYS_INIT(pm_hooks_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

void main(void)
{
    printk("System starting.\n");
    print_clock_state();
    check_pending_irqs();
    check_gpio_sense_config();
    printk("Sleeping now...\n");
    k_sleep(K_FOREVER);
   
}
I expect that after the message "Sleeping now...", I will get a message "pm_policy_next_state() called" followed by "PM: Preparing to suspend..." then "Notifier: system preparing to suspend." But the board is stuck at "Sleeping now..." Here's the RTT Viewer output:
System starting.
HFCLK running: 0
LFCLK running: 0
Checking pending IRQs...
Checking GPIO sense configs...
Sleeping now...
Does anyone have an idea on what I am doing wrong? 
I am using nRF Connect SDK v2.9.0 for VS Code, and Zephyr v3.7.99.

Thanks!


Parents
  • Hello,

    I think you need to change your power state from PM_STATE_SUSPEND_TO_RAM to PM_STATE_SOFT_OFF if you want to implement deep sleep.

    The nRF52810 and other Nordic devices only support two main power modes:
    System ON mode - This is the normal operating mode where the CPU can be put to sleep, but peripherals remain powered.
    System OFF mode - This is the deepest power saving mode available on Nordic devices, which corresponds to PM_STATE_SOFT_OFF in Zephyr's power management states. 
    You can change your code following way
    a. Modifying the pm_policy_next_state() function to return PM_STATE_SOFT_OFF instead of PM_STATE_SUSPEND_TO_RAM:
    const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
    {
        static const struct pm_state_info soft_off_state = {
            .state = PM_STATE_SOFT_OFF,
            .substate_id = 0,
            .min_residency_us = 1000,
            .exit_latency_us = 500,
        };
        printk("pm_policy_next_state() called\n");
        // Replace with your custom condition
        bool ready_to_sleep = true;
        if (ready_to_sleep) {
            return &soft_off_state;
        }
    
        return NULL; // Stay in active state
    }
    b. Updating your pm_state_set() function to handle PM_STATE_SOFT_OFF:
    void pm_state_set(enum pm_state state, uint8_t substate_id)
    {
        if (state == PM_STATE_SOFT_OFF) {
            printk("PM: Preparing to enter system off...\n");
    
            // Configure wake-up GPIO
            nrf_gpio_cfg_input(WAKEUP_PIN, NRF_GPIO_PIN_PULLUP);
            nrf_gpio_cfg_sense_set(WAKEUP_PIN, NRF_GPIO_PIN_SENSE_LOW);
    
            print_clock_state();
            check_pending_irqs();
            check_gpio_sense_config();
    
            // Enter system off mode
            printk("Entering system off mode\n");
            
            // This will put the system into deep sleep
            nrf_regulators_system_off(NRF_REGULATORS);
            
            // Code will not reach here unless system off fails
            printk("ERROR: System off failed\n");
        }
    }
    c. Updating your notifier callbacks to handle PM_STATE_SOFT_OFF
    static void on_pm_entry(enum pm_state state)
    {
        if (state == PM_STATE_SOFT_OFF) {
            //suspend_peripherals();
            printk("Notifier: system preparing to enter system off.\n");
        }
    }
    
    static void on_pm_exit(enum pm_state state)
    {
        if (state == PM_STATE_SOFT_OFF) {
            //resume_peripherals();
            printk("Notifier: system resumed from system off.\n");
        }
    }
    Can try this and let me know if this works?
    Thanks.
    BR
    Kazi
  • Hi Kazi,

    Thank you for your reply. PM_STATE_SOFT_OFF does work but it resets my board upon wake up. My board needs to resume upon wake up. That is why I chose PM_STATE_SUSPEND_TO_RAM. Maybe the term deep sleep is incorrect. I just need my board to consume 20uA during sleep. 

    Also, as I stated in my description, all peripherals are either suspended or are disabled before sleeping.

    Awaiting your reply.

    Regards,

    Edgar

Reply
  • Hi Kazi,

    Thank you for your reply. PM_STATE_SOFT_OFF does work but it resets my board upon wake up. My board needs to resume upon wake up. That is why I chose PM_STATE_SUSPEND_TO_RAM. Maybe the term deep sleep is incorrect. I just need my board to consume 20uA during sleep. 

    Also, as I stated in my description, all peripherals are either suspended or are disabled before sleeping.

    Awaiting your reply.

    Regards,

    Edgar

Children
No Data
Related