GPIO sense wakeup from system on (__WFE or __WFI) not working on nrf9160dk

Hi! I have a problem with waking up from system on using gpio interrupts. Originally I was using SYSTEM OFF and that was working fine but i also wanted to wake the system up with the RTC. Now this works on SYSTEM ON. I can specify a time and the system wakes up by an interrupt from an RTC. But now I want it to work with gpios too but for some reason it does not wake up. This is my code so far:

/*
 * Copyright (c) 2019 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/pm/device.h>
#include <zephyr/sys/poweroff.h>
#include <soc.h>
#include <hal/nrf_gpio.h>
#include <hal/nrf_regulators.h>
#include <zephyr/drivers/gpio.h>
#include <modem/lte_lc.h>
#include <modem/nrf_modem_lib.h>
#include <nrfx_rtc.h>
#include <nrfx_clock.h>




#define BUSY_WAIT_S 2U
#define SLEEP_S 2U
#define NODE_SWITCH DT_ALIAS(sw0)
#define SW0_PIN 6
#define LED0_NODE DT_ALIAS(led0)
#define INPUT_PIN_NODE DT_NODELABEL(input_pin)
#define INPUT_PIN_NODE2 DT_NODELABEL(input_pin2)
#define OUTPUT_PIN_NODE DT_NODELABEL(output_pin)


// static const struct device *switch_dev = DEVICE_DT_GET(NODE_SWITCH);
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec input_pin = GPIO_DT_SPEC_GET(INPUT_PIN_NODE, gpios);
static const struct gpio_dt_spec input2_pin = GPIO_DT_SPEC_GET(INPUT_PIN_NODE2, gpios);
// static const struct gpio_dt_spec output_pin = GPIO_DT_SPEC_GET(OUTPUT_PIN_NODE, gpios);

static const struct gpio_dt_spec output_pin = GPIO_DT_SPEC_GET(OUTPUT_PIN_NODE, gpios);
static const nrfx_rtc_t rtc = NRFX_RTC_INSTANCE(0);

static void rtc_handler(nrfx_rtc_int_type_t int_type)
{
    if (int_type == NRFX_RTC_INT_COMPARE0)
    {
        printk("RTC compare event\n");
    }
    else if (int_type == NRFX_RTC_INT_TICK)
    {
		printk("RTC tick event\n");
    }
}



static void rtc_config(void)
{
    nrfx_err_t err_code;

    //Initialize RTC instance
	printf("RTC instance\n");
	nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG;
    err_code = nrfx_rtc_init(&rtc, &config, rtc_handler);
    if (err_code != NRFX_SUCCESS)
	{
		printk("Error initializing RTC\n");
		k_msleep(1000);
	}
	printf("RTC initialized\n");
    //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
    err_code = nrfx_rtc_cc_set(&rtc,0, 32768*35, true);
    if (err_code != NRFX_SUCCESS)
	{
		printk("Error setting compare channel\n");
	}
	printf("RTC compare channel set\n");

    //Power on RTC instance
    nrfx_rtc_enable(&rtc);
	printf("RTC enabled\n");
}





int main(void)
{	
	uint32_t input_pin_nr = DT_GPIO_PIN(INPUT_PIN_NODE, gpios);
	uint32_t input2_pin_nr = DT_GPIO_PIN(INPUT_PIN_NODE2, gpios);
	printf("latch input pin 16 %d\n", nrf_gpio_pin_latch_get(input_pin_nr));
	printf("latch input pin 13 %d\n", nrf_gpio_pin_latch_get(input2_pin_nr));
	// Toggle LED0 ON
	int ret;
	// Output pin
	if (!gpio_is_ready_dt(&output_pin)) {
		printf("Output pin is not ready\n");
		return 0;
	}

	ret = gpio_pin_configure_dt(&output_pin, GPIO_OUTPUT_ACTIVE);
	if (ret != 0) {
		printf("Configuring GPIO pin failed: %d\n", ret);
		return 0;
	}
	gpio_pin_set_dt(&output_pin, 1);

	if (!gpio_is_ready_dt(&led)) {
		return 0;
	}

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		return 0;
	}

	
	
	
	
	int rc;
	const struct device *const cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));

	if (!device_is_ready(cons)) {
		printk("%s: device not ready.\n", cons->name);
		return 0;
	}

	printk("\n%s system off demo\n", CONFIG_BOARD);

	/* Configure to generate PORT event (wakeup) on input pin interrupt. */
	nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(INPUT_PIN_NODE, gpios),
			   NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(INPUT_PIN_NODE, gpios),
			       NRF_GPIO_PIN_SENSE_LOW);

	nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(INPUT_PIN_NODE2, gpios),
			   NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(INPUT_PIN_NODE2, gpios),
			       NRF_GPIO_PIN_SENSE_LOW);


	//configure GPIOTE interrupt
	NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
	NVIC_EnableIRQ(GPIOTE0_IRQn);


	


	

	


	


	printk("Busy-wait %u s\n", BUSY_WAIT_S);
	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);

	printk("Busy-wait %u s with UART off\n", BUSY_WAIT_S);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);

	printk("Sleep %u s\n", SLEEP_S);
	k_sleep(K_SECONDS(SLEEP_S));

	printk("Sleep %u s with UART off\n", SLEEP_S);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_sleep(K_SECONDS(SLEEP_S));
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);

	printk("Entering system off; Waiting to be awaken from input_pin\n");

	// Turn off output pin before entering system off
	ret = gpio_pin_set_dt(&output_pin, 0);
	if (ret) {
		printf("Error setting output pin: %d\n", ret);
		return 0;
	}

	// Toggle LED0 OFF before entering system off
	ret = gpio_pin_toggle_dt(&led);
	if (ret < 0) {
		return 0;
	}


	// turn off the LTE modem to allow the system to enter full system off.
	
	printf("resetting latch\n");
	nrf_gpio_pin_latch_clear(input_pin_nr);
	nrf_gpio_pin_latch_clear(input2_pin_nr);
	rtc_config();
	nrf_modem_lib_init();
	lte_lc_power_off();
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_msleep(1000);

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

	// NRF_REGULATORS->SYSTEMOFF = 1;
	return 0;
}


void GPIOTE_IRQHandler(void)
{
    // This handler will be run after wakeup from system ON (GPIO wakeup)
    if(NRF_GPIOTE->EVENTS_PORT)
    {
        NRF_GPIOTE->EVENTS_PORT = 0;
        
    }
}


And this is my prj.conf:

# Required to disable default behavior of deep sleep on timeout
CONFIG_PM_DEVICE=y
CONFIG_GPIO=y
# Optional select RAM retention (nRF52 only)
#CONFIG_APP_RETENTION=y
CONFIG_CRC=y
CONFIG_POWEROFF=y
CONFIG_NRFX_RTC0=y
#Below forces us to build the nonsecure image for the nRF9160dk i.e.: "west build -p -b nrf9160dk_nrf9160ns". 
CONFIG_LTE_LINK_CONTROL=y 
#Below means that the modem will not automatically connect to the network on boot, you need to manually enable it of you want to connect to the network. 
#Putting this to "y" will mess with the system off mode current consumption.
#If auto connect is enabled, the MCU will restart itself straight after it has entered system off mode.
CONFIG_LTE_AUTO_INIT_AND_CONNECT=n 
CONFIG_NRF_MODEM_LIB=y
#Below is not crucial to be disabled to allow the system off mode to work. 
CONFIG_DEBUG=n 
CONFIG_PM_DEVICE=y


Thanks in advance!

Best,
Ray

Parents
  • Hello,

    It is generally recommended to use the drivers and services provided by the RTOS whenever possible. However, there are cases where it may make sense or even be necessary to rely on low level drivers or direct register access. In such cases, extra care must be taken to ensure that you are not accessing hardware peripherals that may be used simultaneously by other parts of the system.

    I put together a basic demo sample that uses the Zephyr timer to trigger periodic RTC interrupts, and the zephyr gpio driver for pin interrupts (uses GPIOTE PORT events, see board overlay file). Please take a look at this and see if it is something that can work in your application.

    button_system_off.zip

    Note: I tested it with SDK v2.9.0

    Best regards,

    Vidar

  • Hello, sorry for the late answer!

    Your example works perfectly and i agree that it's better to use RTOS drivers but it is sometimes not clear how it works with the board. For example, how comes that k_timer uses the RTC even though i haven't enabled it in the overlay file or the prj.conf?

Reply Children
Related