nrf54 wakeup using uart pins as gpio.

Using nrf connect 3.1.1

using nrf54L15 evaluation board.

Hi,

I have an application that is using the shell capabilities of the zephyr.  uart and shell is connected to stranded virital com to usb c,

#define UART_TX_GPIO_SPEC       <NRF_PSEL(UART_TX,   1, 4)>
#define UART_RX_GPIO_SPEC       <NRF_PSEL(UART_RX,   1, 5)>
I have succeeded in putting device into sleep mode using a shell command, and i observe minimal current drawn.
Next i wish to wake nrf54 and re- enable uart when receiving an interrupt on rx line P1.05.
I configured the rx pin to be interrupted, put this will not fired.
what am I missing?
Thanks
Dan
// UART
#define UART_TX_GPIO_SPEC       <NRF_PSEL(UART_TX,   1, 4)>
#define UART_RX_GPIO_SPEC       <NRF_PSEL(UART_RX,   1, 5)>
#define UART_RX_GPIO_SPEC_PIN   <&gpio1 5  (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>



&gpio1 {
	sense-edge-mask = <0x00002120>; /* P1.05, P1.08, P1.13 */
};

   
    pad_input:pad_input {
        compatible = "gpio-keys";
		debounce-interval-ms = <150>;                      /* Debouncing time */
        
		uart_rx_pin: uart_rx_pin {
			gpios = UART_RX_GPIO_SPEC_PIN;
			label = "uart rx pin";
			zephyr,code = <INPUT_KEY_1>;
		};
    };
    
    
///
/// UART
///
&uart20 {
	current-speed = <115200>;
	pinctrl-0 = <&uart20_default>;
	pinctrl-1 = <&uart20_sleep>;
	pinctrl-names = "default", "sleep";
};


&pinctrl {
	/omit-if-no-ref/ uart20_default: uart20_default {
		group1 {
			psels = UART_TX_GPIO_SPEC;
		};

		group2 {
			psels = UART_RX_GPIO_SPEC;
			bias-pull-up;
		};
	};

	/omit-if-no-ref/ uart20_sleep: uart20_sleep {
		group1 {
			psels = UART_TX_GPIO_SPEC,
				    UART_RX_GPIO_SPEC;
			low-power-enable;
		};
	};

};
    
    
    
    
#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/led.h>
#include <zephyr/settings/settings.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/reboot.h>
const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart20));

static struct gpio_callback gpio_cb;

static void gpio_isr(const struct device *port,
                     struct gpio_callback *cb,
                     uint32_t pins)
{
    LOG_INF("ISR fired ");
    int x = pm_device_runtime_usage(uart);
    if(x == 0)
    {
        pm_device_runtime_get(uart);
    }
}

void uart_rx_pin_setup(void)
{
    const struct gpio_dt_spec uart_rx_pin = GPIO_DT_SPEC_GET(DT_NODELABEL(uart_rx_pin), gpios); 
   
    gpio_pin_configure_dt(&uart_rx_pin, GPIO_INPUT);

    gpio_pin_interrupt_configure(uart_rx_pin.port,
                                       uart_rx_pin.pin,
                                       GPIO_INT_EDGE_BOTH);

    gpio_init_callback(&uart_rx_pin,
                       gpio_isr,
                       BIT(uart_rx_pin.pin));

    gpio_add_callback(uart_rx_pin.port, &gpio_cb);

}

void uart_sleep(void)
{
    pm_device_runtime_put(uart);
    uart_rx_pin_setup();
}


int main(void)
{

    // Enable runtime power management
    pm_device_runtime_enable(uart);
    pm_device_runtime_get(uart);


    while(1)
    {

        k_sleep(K_SECONDS(10));
    }
 
	return 0;
}

static int shell_disable(const struct shell *shell, size_t argc, char **argv)
{
     uart_sleep(); // disable uart
    return 0;
}


SHELL_STATIC_SUBCMD_SET_CREATE(sleep_cmds,
    SHELL_CMD(shell_disable, NULL, "Disable shell ", shell_disable),
    SHELL_SUBCMD_SET_END
);

SHELL_CMD_REGISTER(uart, &sleep_cmds, "Sleep commands", NULL);
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000

CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
#CONFIG_NRFX_UARTE20=y
#CONFIG_PINCTRL=y
#CONFIG_PINCTRL_NRF=y
#CONFIG_PINCTRL_DYNAMIC=y

# Shell
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_UART_CONSOLE=y

# Logging
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=2
CONFIG_LOG_MODE_MINIMAL=n
#CONFIG_LOG_BACKEND_UART=n
CONFIG_LOG_RUNTIME_FILTERING=y              # adds ability to change log levels at runtime
#CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_LOG_CMDS=y                           # shell commands
CONFIG_LOG_PRINTK=y

# Bluetooth
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="BLE_Advertiser"
CONFIG_BT_DEVICE_APPEARANCE=0
CONFIG_BT_PRIVACY=n
CONFIG_BT_HCI=y


#float support
CONFIG_CBPRINTF_FP_SUPPORT=y                # enables float support in printf functions

# external flash
CONFIG_FLASH=y
CONFIG_SPI_NOR_SFDP_RUNTIME=y               # enable SFDP support for SPI NOR flash
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_FLASH_PAGE_LAYOUT=y                  # enable flash page layout API    
CONFIG_FLASH_SHELL=y
CONFIG_FLASH_MAP=y

# NVS Filesystem
CONFIG_NVS=y
CONFIG_NVS_LOG_LEVEL_DBG=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y

# Input sub system
CONFIG_INPUT=y
CONFIG_INPUT_EVENT_DUMP=y
CONFIG_INPUT_GPIO_KEYS=n
CONFIG_GPIO=y
CONFIG_LED=y
CONFIG_LED_GPIO=y
#CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS MAX is 12
CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS=12 
#CONFIG_NRFX_GPIOTE_LOG=y



# NFC
CONFIG_NFC_T4T_NRFXLIB=y
CONFIG_NFC_NDEF=y
CONFIG_NFC_NDEF_MSG=y
CONFIG_NFC_NDEF_RECORD=y
CONFIG_NFC_NDEF_LE_OOB_REC=y
CONFIG_NFC_NDEF_PARSER=y


# HEAP
#CONFIG_SYS_HEAP_RUNTIME_STATS=y

# CONFIG_PM_PARTITION_SIZE_NVS_STORAGE=0x00

# MAIN TASK MODULE
CONFIG_APP_MAIN_TASK_ENABLED=y
CONFIG_APP_MAIN_TASK_SHORT_QUEUE_LEN=10
CONFIG_APP_MAIN_TASK_MAX_SHORT_MESSAGE_LEN=4
CONFIG_APP_MAIN_TASK_LONG_QUEUE_LEN=4
CONFIG_APP_MAIN_TASK_MAX_LONG_MESSAGE_LEN=128
CONFIG_APP_MAIN_TASK_MAX_NUM_OF_EVENTS=15 

  • for clarity - I am expecting characters sent from PC connected to uart to fire the ISR and waking the uart.  

    in the ISR or main context configure the uart to run again.

    in the past - On nrf5 SDK we have done this manipulation and it work wonderful.

  • Hi,

     

    This topic comes into play when using PM runtime API:

     button with "gpio-keys" not fired when CONFIG_PM_DEVICE_RUNTIME=y 

     

    SHELL will also be a consumer of your uart, so it will keep it on. Try instead of using serial backend for shell, to use RTT instead.

    This function call will create issues:

        gpio_init_callback(&uart_rx_pin,
                           gpio_isr,
                           BIT(uart_rx_pin.pin));
    

     Should point to "gpio_cb", not "uart_rx_pin".

     

    If you choose to go with PM runtime API, you should enable the "uart_rx_pin.port" via pm_device_runtime_get() when disabling the uart.

    If you choose to use PM without runtime, you should suspend the uart, then setup your gpio callback.

     

    Kind regards,

    Håkon

  • I confirmed this works as I needed.

    I suspend uart, then configure rx pin as interrupt and it work.

    after interrupt fires, i disable it and re enable uart.

    thanks.

    static int configure_uart_rx_wakeup(void)
    {
        if (!device_is_ready(uart_rx_pin.port)) {
            return -ENODEV;
        }

        int ret = gpio_pin_configure_dt(&uart_rx_pin,
                                        GPIO_INPUT | GPIO_PULL_UP);
        if (ret) {
            return ret;
        }

        ret = gpio_pin_interrupt_configure(uart_rx_pin.port,
                                           uart_rx_pin.pin,
                                           GPIO_INT_EDGE_BOTH);
        if (ret) {
            return ret;
        }

        k_work_init_delayable(&work, work_thread);

        gpio_init_callback(&gpio_cb,
                           gpio_isr,
                           BIT(uart_rx_pin.pin));

        ret = gpio_add_callback(uart_rx_pin.port, &gpio_cb);
        if (ret) {
            return ret;
        }

        return 0;
    }
  • Hi!

     

    narfster said:

    I confirmed this works as I needed.

    I suspend uart, then configure rx pin as interrupt and it work.

    after interrupt fires, i disable it and re enable uart.

    thanks.

    That is great to hear, and thank you so much for sharing your proposed code. Hope you have a wonderful day!

     

    Kind regards,

    Håkon

Related