How to put the UART to sleep (low power mode)

I believe it started with SDK 2.0.0 but the board config files now define different ways to specify how hardware is to run.

&pinctrl {

    uart1_default: uart1_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 26)>,
				<NRF_PSEL(UART_RTS, 0, 29)>;
		};
		group2 {
			psels = <NRF_PSEL(UART_RX, 0, 27)>,
				<NRF_PSEL(UART_CTS, 0, 28)>;
			bias-pull-up;
		};
	};

	uart1_sleep: uart1_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 26)>,
				<NRF_PSEL(UART_RX, 0, 27)>,
				<NRF_PSEL(UART_RTS, 0, 29)>,
				<NRF_PSEL(UART_CTS, 0, 28)>;
			low-power-enable;
		};
	};
	
&uart1 {
	status = "okay";
	pinctrl-0 = <&uart1_default_alt>;
	pinctrl-1 = <&uart1_sleep_alt>;
	pinctrl-names = "default", "sleep";
};

I took this to mean that I can dynamically toggle these configurations from "uart1_default" to "uart1_sleep" from perhaps a function call. Did find the pinctrl_apply_state() function call but it's return a -2 that state id doesn't exist even though it is defined. Here's my code.

uart = device_get_binding("UART_1");
int err = pinctrl_apply_state(uart->config, PINCTRL_STATE_SLEEP); // Returns -2
Any ideas?
  • Hello,

    You are correct, 'pin control' was introduced in v2.0.0. However, it only controls the pin state. To get the UART to a low power state, you need to use it via the power management subsystem. The code snippet below shows an example of how you can use the API to power down your UART.

    prj.conf

    # Device power management
    CONFIG_PM_DEVICE=y

    Code 

    #include <zephyr/pm/device.h>
    
    ...
    
    void some_function(void) 
    {
        int err;
        /* Power down the UART device */
        err = pm_device_action_run(<uart_dev>, PM_DEVICE_ACTION_SUSPEND);
        if (err) {
            printk("pm_device_action_run() failed (%d)\n", err);
        }
        
        ...
        /* Power it on again */
        err = pm_device_action_run(<uart_dev>, PM_DEVICE_ACTION_RESUME);
        if (err) {
            printk("pm_device_action_run() failed (%d)\n", err);
        }

    Best regards,

    Vidar

  • Sweet, I got it to compile! Will let you know. Big thanks!

  • On the 52840, will "pm_device_action_run(<uart_dev>, PM_DEVICE_ACTION_SUSPEND);" together with nrf_power_system_off(NRF_POWER); reduce the power to 0.95uA, per the datasheet

  • Yes, you should be able to reduce the sleep current to around 0.95 uA by entering System OFF mode. The important thing to ensure before entering this mode is that you don't have any floating inputs, as the GPIO configurations are preserved in this mode. Using the command "pm_device_action_run(<uart_dev>, PM_DEVICE_ACTION_SUSPEND);" will handle the pin configuration assigned to the UART interface. That is, set the pins as "input, disconnect" to prevent current leakge.

  • My apologies, I don't quite follow. Are you saying I should make a change to the below to indicate "input, disconnect"? I tried replacing low-power-enable; with input,disconnect but that doesn't compile.

    &pinctrl {

        uart0_default: uart0_default {
            group1 {
                psels = <NRF_PSEL(UART_TX, 0, 26)>,
                    <NRF_PSEL(UART_RTS, 0, 27)>;
            };
            group2 {
                psels = <NRF_PSEL(UART_RX, 0, 0)>,
                    <NRF_PSEL(UART_CTS, 0, 6)>;
                bias-pull-up;
            };
        };

        uart0_sleep: uart0_sleep {
            group1 {
                psels = <NRF_PSEL(UART_TX, 0, 26)>,
                    <NRF_PSEL(UART_RX, 0, 0)>,
                    <NRF_PSEL(UART_RTS, 0, 27)>,
                    <NRF_PSEL(UART_CTS, 0, 6)>;
                low-power-enable;
            };
        };
Related