nRF52840 Power Management when idle

Greetings,

I am looking into power management options for our custom board using the nrf52840 with the nRF Connect SDK.

I wanted to ask for a clarification if possible.

Do I have to enable anything other than CONFIG_PM=y option to enable the system to go into low power mode when the idle thread is running?

Do I have to use the CAF for that to happen?

Thank you!

Best Regards,

Stavros

  • Hi Savros,

    You don't have to do anything to actively put the chip into System ON idle mode. After all threads are finished (or threads are put to sleep by k_msleep() or similar), the CPU enters the idle thread, and wakes up on the next event. This happens regardless of the CONFIG_PM state.

    CONFIG_PM is set to =y by default in the samples, but actively turning this off does not prevent the CPU from entering the idle thread. What you want to do is to use CONFIG_PM_DEVICE to turn off peripherals that are still active after the CPU enters the idle thread.

    Here is an example using the zephyr/sample/basic/blinky:

    • When you run this out of the box on a nRF52 DK you can see that the idle current is around 500-800 uA (depending on which chip you use).
    • The main loop enters k_msleep() after toggling the GPIO.
    • k_msleep() lets the CPU enter the idle thread, and the CPU will now be in system ON idle until the timer runs out after SLEEP_TIME_MS milliseconds (or another thread wakes up the CPU)

    But why does the example still consume 500-800 uA? This is because logging is enabled, and logging uses the UART peripheral.

    There are two ways to turn off logging.

    1. The easiest way is to put CONFIG_SERIAL=n in the prj.conf file as this will shut down all the UART peripherals. But then you can not use UART later.
    2. Use the power management API:
      • Add CONFIG_PM_DEVICE=y to prj.conf
      • Add #include <zephyr/pm/device.h> to main.c
      • The following code will turn off the logging module (and UART):
        const struct device *uart0_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
        pm_device_action_run(uart0_dev, PM_DEVICE_ACTION_SUSPEND);

    The resulting main.c for the zephyr/samples/basic/blinky example will look something like this:

    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/pm/device.h>
    
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME_MS   1000
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    
    /*
     * A build error on this line means your board is unsupported.
     * See the sample documentation for information on how to fix this.
     */
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    const struct device *uart0_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
    
    void main(void)
    {
    	int ret;
    
    	ret = pm_device_action_run(uart0_dev, PM_DEVICE_ACTION_SUSPEND);
    	if (ret < 0) {
    		return;
    	}
    
    	if (!device_is_ready(led.port)) {
    		return;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return;
    	}
    
    	while (1) {
    		ret = gpio_pin_toggle_dt(&led);
    		if (ret < 0) {
    			return;
    		}
    		k_msleep(SLEEP_TIME_MS);
    	}
    }

    And prj.conf:

    CONFIG_GPIO=y
    CONFIG_PM_DEVICE=y

    I tested the code above on a DK and the idle current is around 2 uA. If I set CONFIG_PM to =n the idle current is still around 2 uA.

    Best regards,
    Stian

  • Hi Stian,

    Thank you very much for the very thorough and helpful response!

    This is exactly the information I was looking for to confirm! 

    I will need a little bit of time to check if I have any other pending/followup questions and I will close the ticket asap.

    Thanks again!

    Best regards,

    Stavros

Related