Multiple GPIO interrupts from SYSTEM_SOFT_OFF

OK, so it seems if you accidently hit "VERIFY ANSWER" your discussion gets locked and you can no longer comment, nor reverse that process.  So, I've had to start a new discussion in order to keep on with the old one:

https://devzone.nordicsemi.com/f/nordic-q-a/88286/multiple-gpio-interrupts-from-system_soft_off

What I wanted to say, rather than verify the answer, was:

Hi Simon,

Firstly, I seem to have accidently set your reply to "verified answer" and I can't seem to work out how to reverse that.

Anyway, I'm actually using the nRF Connect SDK (V1.9.1) and Zephyr, and may be 'cross-pollinating" by using those API's.  But I did ask on here if they were the only ones to use and didn't ever get an answer, so have just made them work for the moment.

What I'm seeing I don't think will benefit from button debounce, but I may be wrong.  What seems to be happening is that the first button press will trigger the device out of SYSTEM_OFF mode, and then my code starts running.  But any subsequent button press seems to interrupt the code that is being run and boot the device out of SYSTEM_OFF mode, even though I don't think it is actually in that mode.  This then seems to scramble all the registers I am using to determine how/why it came out of SYSTEM_OFF, and then I can't get it to do what it is supposed to do.

What I want to be able to do is the moment any GPIO triggers an exit from SYSTEM_OFF, that I disable this function so that any subsequent button presses will have no impact.  Essentially a bit like disabling interrupts whilst you service an exisiting interrupt.

But I can't find anywhere online where the functionality to achieve this is described.

I'd be happy to go with a low power mode rather than SYSTEM_OFF if it makes achieving what I want easier.  I can probably deal with quiescent currents of < 5uA.  Its just I couldn't actually find any helpful Zephyr/nRF Connect SDK low power examples, and everyone I have asked seems to look at me blankly.

I had a quick look at the example you listed in the original post.  Can't immediately understand much of it, but I'll go and have a more detailed look.  Also, not sure how relevant it is given I'm not using the nRF5 SDK

Cheers,

Mike

  • Hi Simon,

    So, I'm using the system_off example in ...v1.9.1\zephyr\samples\boards\nrf\system_off and the only modifications I have made are:

    1. Configure P0.14 to be an output and set it low on start up

    2. Removed all the stuff about enabling/disabling the UART and putting it into various low(er) power modes

    3. Set P0.14 just prior to entering System Off

    4. Added a k_sleep() statement straight after the call to go into System Off, otherwise it doesn't work (a known issue with the example code)

    The delay between my button0 signal going low, and my button1 signal going low, which is essentially measuring the time it takes to get to the first line in my main.c, is around 400msec, although it does vary a bit

    This is my main.c in full.

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <zephyr.h>
    #include <device.h>
    #include <init.h>
    #include <pm/pm.h>
    #include <pm/device.h>
    #include "retained.h"
    #include <hal/nrf_gpio.h>
    
    #define CONSOLE_LABEL DT_LABEL(DT_CHOSEN(zephyr_console))
    
    #define BUSY_WAIT_S 2U
    #define SLEEP_S 2U
    
    /* Prevent deep sleep (system off) from being entered on long timeouts
     * or `K_FOREVER` due to the default residency policy.
     *
     * This has to be done before anything tries to sleep, which means
     * before the threading system starts up between PRE_KERNEL_2 and
     * POST_KERNEL.  Do it at the start of PRE_KERNEL_2.
     */
    static int disable_ds_1(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    
    	pm_constraint_set(PM_STATE_SOFT_OFF);
    	return 0;
    }
    
    SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);
    
    void main(void)
    {
    	/* Configure button 1 (P0.14) to be an output to allow start up delay measurement */
    	nrf_gpio_cfg_output(DT_GPIO_PIN(DT_NODELABEL(button1), gpios));
    
    	/* Toggle P0.14 output on startup.  P0.14 is linked to button1, which has a resistor pull-up, so need to clear it */
    	nrf_gpio_pin_clear(DT_GPIO_PIN(DT_NODELABEL(button1), gpios));
    
    	printk("\n%s system off demo\n", CONFIG_BOARD);
    
    	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
    		bool retained_ok = retained_validate();
    
    		/* Increment for this boot attempt and update. */
    		retained.boots += 1;
    		retained_update();
    
    		printk("Retained data: %s\n", retained_ok ? "valid" : "INVALID");
    		printk("Boot count: %u\n", retained.boots);
    		printk("Off count: %u\n", retained.off_count);
    		printk("Active Ticks: %" PRIu64 "\n", retained.uptime_sum);
    	} else {
    		printk("Retained data not supported\n");
    	}
    
    	/* Configure to generate PORT event (wakeup) on button 1 press. */
    	nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
    			   NRF_GPIO_PIN_PULLUP);
    	nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
    			       NRF_GPIO_PIN_SENSE_LOW);
    
    	/* Reset P0.14 */
    	nrf_gpio_pin_set(DT_GPIO_PIN(DT_NODELABEL(button1), gpios));
    
    	printk("Entering system off; press BUTTON1 to restart\n");
    
    	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
    		/* Update the retained state */
    		retained.off_count += 1;
    		retained_update();
    	}
    
    	/* Above we disabled entry to deep sleep based on duration of
    	 * controlled delay.  Here we need to override that, then
    	 * force entry to deep sleep on any delay.
    	 */
    	pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
    	k_sleep(K_SECONDS(SLEEP_S));
    	printk("ERROR: System off failed\n");
    	while (true) {
    		/* spin to avoid fall-off behavior */
    	}
    }
    

    Regards,

    Mike

  • Putting the pin toggle in a SYS_INT call in either of the PRE_KERNEL stages (I've tried both PRE_KERNEL_1 and PRE_KERNAL_2) drops the start up delay down to < 400usec, which is more what I would have expected to see.

    I think if I can get my start-up delay to < 10msec, I can make things work for my application

    Cheers,

    Mike

  • Hi Mike

    After looking through your main file and discussing this with a couple of colleagues, I still find this strange. It does not seem to be the delays in HW causing this, but rather something set in the application. My guess would be that some peripheral is taking time. Can you upload your configuration file (.conf) so we can make sure it doesn't enable any peripheral taking extra time to start up. Because 400ms is too high for just starting up the device.

    Best regards,

    Simon

  • Hi Simon,

    Not sure how to actually attach files on here.  But this is my proj.conf file:

    CONFIG_PM=y
    # 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
    

    And this is the kconfig file:

    # Copyright (c) 2021 Nordic Semiconductor ASA
    # SPDX-License-Identifier: Apache-2.0
    
    mainmenu "Nordic SYSTEM_OFF demo"
    
    config APP_RETENTION
    	bool "Enable state retention in system off"
    	depends on SOC_COMPATIBLE_NRF52X
    	help
    	  On some Nordic chips this application supports retaining
    	  memory while in system off.  Select this to enable the
    	  feature.
    
    source "Kconfig.zephyr"

    I've not changed either of these from the defaults that came with that example code.

    I am using VSC with the nRF Connect extension.  Not sure if that is causing some issues

    Cheers,

    Mike

  • Hi Simon,

    I commented on another thread, that appeared related to my issues, and one of the other Nordic Engineers proposed some reasoning behind the ~ 400msec delay:

     RE: PIN_CNF Sense change state 

    I am actually using nRF Connect within VSC - but I assume this is what Hakon is referring to when he mentions "NCS"?

    Seems this implements the LFCLK by default, regardless of whether I am using BLE at the time or not, which is what is causing the delay.  So, I was kind of on the right track.

    I have moved where I read the values of RESETREAS and LATCH into a SYS_INIT call in PRE_KERNEL_2 to overcome the 400msec delay issue, so I think I can park that part of the problem and say "its resolved"

    The issue I am still struggling with is the LATCH being reset if I get two GPIO triggers within this software start up period.  In my actual application, this is very likely to happen.

    What I've uncovered is that if I have

    CONFIG_BOOTLOADER_MCUBOOT=y

    in my proj.config file, then the problem of the LATCH register getting reset with the second GPIO trigger signal exists.  If I don't have this in my prof.config file, then the LATCH signal remains intact and I can successfully grab the value of that in my SYS_INIT routine.

    I am writing 

        NRF_GPIO->PIN_CNF[13] &= 0x02;
    as the very first thing I do in my SYS_INIT function, so that the relevant GPIO is disconnected as an input and the SENSE is also disabled.  I'm doing this in an attempt to stop any subsequent reset triggers from the GPIO once the device has come out of System OFF.  But with the CONFIG_BOOTLOADER_MCUBOOT=y statement present in my conf file, this appears to have no impact.  I will see a second reboot of the chip.
    I'm even putting a 2sec wait at the end of my main.c, prior to putting the chip back into System OFF, so that I know its not actually going into System OFF when the second GPIO trigger comes along, but that's not changing the behaviour I am seeing.  The issue seems to be that if I get a second GPIO trigger prior to my main.c code starting up, and with CONFIG_BOOTLOADER_MCUBOOT=y, I will get LATCH reset to zero unexpectantly.
    I am setting CONFIG_BOOTLOADER_MCUBOOT=y because in my final application I need to have DFU functionality, and this was a requirement as indicated in the example code for DFU.
    Any ideas how to get around this LATCH issue?

    Cheers,
    Mike
Related