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...

Parents
  • 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. */

Reply
  • 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. */

Children
No Data
Related