Disable peripherals and GPIOs before sleep and enable back after wakeup

My task is to implement the below functionality and shared the below code

Disable peripherals and GPIOs before sleep and enable back after wakeup

#include <zephyr/kernel.h>
#include <zephyr/pm/pm.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/devicetree.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/devicetree/gpio.h>
#include <zephyr/sys/poweroff.h>
#include <zephyr/device.h>
#include <zephyr/toolchain/common.h>
#include "user_gpio_task.h"
#include <zephyr/sys/__assert.h>
#include <nrfx_wdt.h>
#include <string.h>
#include <zephyr/sys/printk.h>

/* Get the node ID for the sw4 button alias */
#define BUTTON_NODE DT_ALIAS(sw4)

// Ensure the button node exists and is ready at compile time
BUILD_ASSERT(DT_NODE_HAS_STATUS(BUTTON_NODE, okay), "sw4 button node is not ready");

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);
static struct gpio_callback button_cb_data;

//global volatile flag variable
static volatile bool button_pressed = false;

// Use a semaphore to signal a button press event
static K_SEM_DEFINE(button_press_sem, 0, 1);

/* Interrupt callback function for the button pin */
// This function executes when the interrupt is triggered
void button_pressed_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
printk("Button interrupt triggered! Waking up...\n");
// Set the flag to indicate the button was pressed
// button_pressed = true;
k_sem_give(&button_press_sem);
}

void sleep_mode_enable(void)
{
printk("NORA-B2 Button Interrupt Example\n");
printk("Button1 (SW4) is connected to P1.13.\n");

// Check if the GPIO device is ready
if (!gpio_is_ready_dt(&button))
{
printk("GPIO device for button is not ready!\n");
return;
}

// Configure the pin for interrupt and as a wake-up source
int ret = gpio_pin_configure_dt(&button, GPIO_INPUT | GPIO_PULL_UP);
if (ret < 0)
{
printk("Error configuring wake-up pin: %d\n", ret);
return;
}

// Configure the pin interrupt to trigger on a low level (e.g., button press)
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
if (ret < 0)
{
printk("Error configuring wake-up interrupt: %d\n", ret);
return;
}

// Initialize and add the interrupt callback
gpio_init_callback(&button_cb_data, button_pressed_callback, BIT(button.pin));
ret = gpio_add_callback(button.port, &button_cb_data);
if (ret < 0)
{
printk("Error adding wake-up callback: %d\n", ret);
return;
}

printk("Entering low-power idle state. Press the button to wake up.\n");

// Block the thread until the semaphore is given by the interrupt callback.
// The OS will automatically put the CPU into a low-power idle state.
k_sem_take(&button_press_sem, K_FOREVER);

printk("The main thread was woken up!\n");

// The callback and GPIO config are no longer needed
gpio_remove_callback(button.port, &button_cb_data);
gpio_pin_configure_dt(&button, GPIO_DISCONNECTED);

while (1)
{
printk("Normal application logic running...\n");
k_sleep(K_SECONDS(2));
}
}

int disable_peripheral(const struct device *dev) {
if (dev) {
return pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
}
return -ENODEV;
}

int disable_all_peripherals(void) {
// You would add calls to disable all peripherals used in your application
// Example:
//disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(i2c21)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(i2c30)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(spi21)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(spi22)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(uart00)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(uart20)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(uart21)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(gpio0)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(gpio1)));
disable_peripheral(DEVICE_DT_GET(DT_NODELABEL(gpio2)));
printk("Disabling all peripherals...\n");
return 0;
}

int disable_all_gpios(void) {
// This is highly board-specific. For the EVK-NORA-B2, you would need to iterate
// through all GPIOs (e.g., from P0 and P1) and configure them as input with no pull
// to prevent leakage currents on floating pins.
printk("Disabling all non-wakeup GPIOs...\n");
return 0;
}

// --- Deep Sleep Enable Function ---

void deep_sleep_mode_enable(void)
{
int ret;

printk("NORA-B2 Deep Sleep (System OFF) Example\n");

// Step 1: De-initialize all peripherals and turn off LEDs.
disable_all_peripherals();
// AllLedsOff(); // Add a function to turn off all LEDs

// Step 2: Configure the wake-up GPIO.
if (!gpio_is_ready_dt(&button))
{
printk("Error: Wakeup GPIO device is not ready!\n");
return;
}

// We rely on the `zephyr,wakeup-source` property in the DTS to configure the pin.
// This function tells the power management system to enable this device as a wake-up source.
ret = pm_device_wakeup_enable(button.port, true);
if (ret < 0) {
printk("Error enabling device wakeup: %d\n", ret);
return;
}

// Step 3: Disable all other GPIOs to prevent leakage current.
disable_all_gpios();

// Step 4: Enter the deep sleep mode (System OFF).
printk("Entering deep sleep (System OFF). Press the button to reboot.\n");

// This call puts the MCU into the deepest sleep state. Execution stops here.
// The next line of code will only be reached after a full reboot.
pm_device_state_set(button,PM_DEVICE_STATE_OFF);

// This part of the code is unreachable during normal execution of deep sleep.
// It will only be executed if the system fails to enter System OFF.
printk("Failed to enter deep sleep mode.\n");
while(1) { k_sleep(K_FOREVER); }
}

// void wakeup_handler_from_deep_sleep(void)
// {
// // This function should be called at the beginning of your main()
// // to check if the system woke up from deep sleep.
// uint32_t reset_cause = nrf_power_resetreas_get();

// if (reset_cause & NRF_POWER_RESETREAS_SYSOFF_MASK) {
// printk("System woke up from deep sleep!\n");
// }

// // Clear the reset reason flags
// nrf_power_resetreas_clear(reset_cause);
// }



I am getting a below error, am i using the correct function to implement or is there any other function compatible with 

pm_system_state_set(button,PM_DEVICE_STATE_OFF) to use so that nrf54l15 goes in to deepest sleep state  ?



C:/Project/HVPP/hvpp_BLE_application/src/drivers/Sleep/SleepMode.c:207: undefined reference to `pm_system_state_set'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

  • I am using the below dts code for the above functionality, suggest if any modifications in this part of code as well ?

    # Minimal configuration for HVPP BLE application

    # Enable kernel polling (required for k_poll functions)
    CONFIG_POLL=y

    # Enable threading
    CONFIG_MULTITHREADING=y

    # Enable printk for debugging
    CONFIG_PRINTK=y

    # Thread debugging and monitoring
    CONFIG_THREAD_MONITOR=y
    CONFIG_THREAD_NAME=y
    CONFIG_THREAD_STACK_INFO=y

    # Stack protection
    CONFIG_STACK_SENTINEL=y
    ## UART / Console configuration
    CONFIG_SERIAL=y
    CONFIG_CONSOLE=y
    CONFIG_UART_CONSOLE=y
    CONFIG_UART_ASYNC_API=y

    CONFIG_PM=y

    # Enable System Power Management
    CONFIG_PM_DEVICE=y

    CONFIG_PM_POLICY_CUSTOM=y

    #Enable GPIO driver
    CONFIG_GPIO=y

    # Enable the specific timer instance (TIMER22) which uses the NRFX HAL
    # The alias 'my_hw_timer' is used in the application code
    CONFIG_NRFX_TIMER22=y

    # Enable the logging subsystem for console output
    #CONFIG_LOG=y
    #CONFIG_LOG_DEFAULT_LEVEL=4
    #CONFIG_LOG_MODE_DEFERRED=y

    CONFIG_NRFX_UARTE0=n
    CONFIG_NRFX_UARTE2=n

    CONFIG_MAIN_STACK_SIZE=8192
    CONFIG_I2C=y
    CONFIG_I2C_NRFX=y
    CONFIG_SPI=n
    CONFIG_ADC=n
  • Hi,

    C:/Project/HVPP/hvpp_BLE_application/src/drivers/Sleep/SleepMode.c:207: undefined reference to `pm_system_state_set'
    collect2.exe: error: ld returned 1 exit status
    ninja: build stopped: subcommand failed.

    The error is because you are trying to call a function pm_system_state_set() that is not defined. This does not exist in the SDK. Did you mean pm_device_state_set(), or is it a function you have defined yourself? If so note that pm_device_state_set() is deprecated, and pm_device_action_run() should be used to suspend and resume (you can see some examples of that by searching in the code). 

    I recommend you also have a look at the documentation for Device Runtime Power Management.

Related