This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

GPIO PORT Event Locking up

We have been using gpiote low_accuracy port events on battery powered devices for years without any issues, but now we bumped into a strange issue.

Details: 8 PORT (hi_accuracy = false) interrupts are enabled. 5 of them are connected to ext radio chip (in NRF_GPIOTE_POLARITY_LOTOHI configuration) and 3 of them are some buttons with large debounce capacitors and with external pullups driven by other gpio pins, in NRF_GPIOTE_POLARITY_TOGGLE mode. Only sdk api functions are used. Using nrf52832 QFAAE0, SDK 15.3, Segger Embedded Studio 4.22, OSX 10.14.4, custom board with crystals and DCDC.

Problem: No port interrupts coming (port interrupt lockup). No port irq's are coming in after device shutdown and gpio button wakeup (after some hard_to_reproduce combination of pin initialization and transitions). nrfx_gpiote_irq_handler() is never called after this, even though pin input states are changing properly when reading with nrf_gpio_pin_read().

Reason: DETECT signal in GPIO module is constantly high - there cant be another rising edge to call nrfx_gpiote_irq_handler(). Thus there is no mechanism to change the reason of DETECT signal (inverting relevant PINx.CNF.SENSE states in nrfx_gpiote_irq_handler()).

Proof: after resetting LATCH register by calling NRF_P0->LATCH = 0xFFFFFFFF, the register is not fully cleared (the datasheet states that LATCH register is not cleared if PINx.DETECT is high). LATCH register readout (after resetting it) changes when pushing the buttons (relevant bits are cleared and come back when button is released). Note: LATCH is only used as a probe to prove the reason of problem, I'm not using LDETECT.

To reproduce the problem, device has to be put to sys-off (deinit leaves main button port event initialised for wakeup), then after wakeup caused by the button (when in debug build) or nfc (in release build) the LATCH register has bits on after clearing and port irqs are not coming in. I dont remember ever seeing such lockup state occure during runtime, only now during startup. This strange behavior is very fragile - almost any change to anything (and sometimes no change at all) hides the problem, so its very hard to reproduce the problem on DK.

Assumption: It seems that it has to do with timings or something, because release and debug builds behavior differs slightly. It might be that in nrfx_gpiote_in_event_enable() the pin state is read, then a transition happens on that same pin and then nrf_gpio_cfg_sense_set() applies an outdated state. It seems to me that SDK does nothing to handle the unlikely state change that can happen in timewindow between reading the pin state and setting sense polarity (PINx.CNF.SENSE). Maybe my assumptions are wrong, but I dont dare to ship devices with this...

  • Hi,

     

    Proof: after resetting LATCH register by calling NRF_P0->LATCH = 0xFFFFFFFF, the register is not fully cleared (the datasheet states that LATCH register is not cleared if PINx.DETECT is high). LATCH register readout (after resetting it) changes when pushing the buttons (relevant bits are cleared and come back when button is released). Note: LATCH is only used as a probe to prove the reason of problem, I'm not using LDETECT.

     The LATCH register is an accumulative register, in the sense that its not cleared on wakeup from systemoff, as shown in this table:

    https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52832.ps.v1.1/power.html?cp=3_2_0_17_7#unique_832471788

    And LATCH has a couple of erratas on how to be handled:

    Writes to LATCH register take several CPU cycles to take effect

    Bits in GPIO LATCH register are incorrectly set to 1

     

    Could one of these be the source of the issue you're seeing?

     

    That still does not account for the problem-description, where you get into a state where the device is going to sleep with DETECT signal constantly high.

    Can you confirm if sleep in this case is systemoff? Because in that state, there are no clocks running, and nrfx_gpiote uses the CPU to emulate rising and falling events when configured with NRF_GPIOTE_POLARITY_TOGGLE mode, by inverting the _SENSE field on the GPIO that caused the interrupt.

    Note that a interrupt, and a wakeup condition, should be separated when the device is in SYSTEMOFF mode, as SYSTEMOFF will generate a reset on wake up.

    Note 2: if testing systemoff in debug mode, you will enter "emulated systemoff", which is not the same as "the real deal", as described here: https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52832.ps.v1.1/power.html?cp=3_2_0_17_1_0#unique_1199040052

      

    Kind regards,

    Håkon

  • Hi.

    I figured it out. So the problem was misuse of api due to misleading documentation of SDK.

    I'm using nrfx_gpiote_in_init(pin ,NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(false), my_callback);

    When I look at the documentation hints (see below) in SDK I'm assuming that gpiote port event underneath handles both edges and just filters out the falling edges. There are no warnings whatsoever.

    The truth is that LOTOHI port irq assumes short guaranteed pulses - when the pin is left on the internal polarity is not switched and no other pin can generate another port event. The external radio I was using was always giving very short interrupt pulses except rarely at its initialisation it stayed on and all other interrupts went missing.

    This behaviour is clearly seen in void nrfx_gpiote_irq_handler(void) in nrfx_gpiote.c line 766 (SDK 15.3) that internally polarity is inverted only on NRF_GPIOTE_POLARITY_TOGGLE, not for NRF_GPIOTE_POLARITY_LOTOHI and NRF_GPIOTE_POLARITY_HITOLO.

    if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
    {
    	nrf_gpio_pin_sense_t next_sense =
    		(sense == NRF_GPIO_PIN_SENSE_HIGH) ?
    		NRF_GPIO_PIN_SENSE_LOW :
    		NRF_GPIO_PIN_SENSE_HIGH;
    	nrf_gpio_cfg_sense_set(pin, next_sense);
    	++repeat;
    
    }

    Solution

    There should either be strict warnings for this restriction or for all interrupt polarities pin's sensing state should be internally changed to allow other interrupts to work independently. 
    From my point of view no external chip can be trusted to always clear its pins and risking losing all other low power inputs is unacceptable. For now Im just using NRF_GPIOTE_POLARITY_TOGGLE and filter out the edge i need.

    Documentation 
    /**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect low-to-high transition. * @details Set hi_accu to true to use IN_EVENT. */ #define NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(hi_accu) \ { \ .is_watcher = false, \ .hi_accuracy = hi_accu, \ .pull = NRF_GPIO_PIN_NOPULL, \ .sense = NRF_GPIOTE_POLARITY_LOTOHI, \ }

    /**
    * @enum nrf_gpiote_polarity_t
    * @brief Polarity for the GPIOTE channel.
    */
    typedef enum
    {
    NRF_GPIOTE_POLARITY_LOTOHI = GPIOTE_CONFIG_POLARITY_LoToHi, ///< Low to high.
    NRF_GPIOTE_POLARITY_HITOLO = GPIOTE_CONFIG_POLARITY_HiToLo, ///< High to low.
    NRF_GPIOTE_POLARITY_TOGGLE = GPIOTE_CONFIG_POLARITY_Toggle ///< Toggle.
    } nrf_gpiote_polarity_t;

    #define GPIOTE_CONFIG_POLARITY_LoToHi (1UL) /*!< Task mode: Set pin from OUT[n] task. Event mode: Generate IN[n] event when rising edge on pin. */

Related