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

NRF51422 - GPIOTE PORT events stop being generated - why?

I am encountering a problem with GPIOTE PORT events on the NRF51422 in a custom design. The chip occasionally seems to be able to get into a state in which the registers are set up correctly for a PORT event and interrupt to be generated, but despite this no event occurs.

I have a pin which is connected to a push switch and external pullup resistor, which normally triggers various visible actions on the system. The symptom of the problem is that the system stops responding to the button.

If I hold the switch down when the problem is occuring and then halt the CPU, I can see in the debugger that:

  • PIN_CNF is set to input, connected, no pull, standard drive, sense low.
  • The IN register for the pin shows the input level as low.
  • The GPIOTE INTENSET register shows the PORT enable bit set.
  • EVENTS_PORT is zero.

If I set a breakpoint at the GPIOTE interrupt, then:

  • With the system working normally, the breakpoint is hit when the button is pressed and shows the registers in the state above, except that EVENTS_PORT reads 1, indicating the event has occured.

  • When the problem is occuring, the breakpoint is not hit, and if I halt the CPU I see the registers in the same state as above with EVENTS_PORT reading zero.

I can reproduce this problem on two copies of the board.

What could be preventing this event from being generated?

Parents
  • I am already using the app_gpiote module, which toggles SENSE in the GPIOTE_IRQHandler as you describe.

    I'm not using app_button since I had different requirements, but there is no such workaround in app_button, the app_button module just assumes that app_gpiote is working correctly.

    The situations that seem to trigger this problem are ones where several GPIOs may change in succession (in particular, I get it when USB power is removed from my device and various signals from the USB and power management chips all change together or in close succession).

    I had previously reviewed the app_gpiote code suspecting a race condition, and concluded from my understanding of the hardware at that time that the code should be OK, because even if the SENSE levels got out of sync with the pin states, a new event would just be generated and the ISR would be repeated.

    If you are telling me however that the PORT event is edge-triggered from the DETECT signal rather than level-triggered, then there is a race condition here.

    The sequence of events is as follows:

    1. Pin set to sense high.
    2. Pin goes high, DETECT asserted, PORT event set.
    3. GPIOTE_IRQHandler called
    4. GPIOTE_IRQHandler clears PORT event.
    5. GPIOTE_IRQHandler reads IN register.
    6. GPIOTE_IRQHandler sets SENSE level according to value read from IN.

    If the pin goes low at any time after step 2 and before step 5, then the SENSE level will not be changed from its previous value and the DETECT signal will remain asserted, allowing no further PORT events to be generated.

    When multiple signals are being monitored the problem becomes only more likely, because any of them can change in this window and cause DETECT to remain asserted.

    What troubles me is that with no way to read the DETECT signal from the CPU, I cannot even readily see how this can be fixed. It seems like a fundamental flaw in the design.

Reply
  • I am already using the app_gpiote module, which toggles SENSE in the GPIOTE_IRQHandler as you describe.

    I'm not using app_button since I had different requirements, but there is no such workaround in app_button, the app_button module just assumes that app_gpiote is working correctly.

    The situations that seem to trigger this problem are ones where several GPIOs may change in succession (in particular, I get it when USB power is removed from my device and various signals from the USB and power management chips all change together or in close succession).

    I had previously reviewed the app_gpiote code suspecting a race condition, and concluded from my understanding of the hardware at that time that the code should be OK, because even if the SENSE levels got out of sync with the pin states, a new event would just be generated and the ISR would be repeated.

    If you are telling me however that the PORT event is edge-triggered from the DETECT signal rather than level-triggered, then there is a race condition here.

    The sequence of events is as follows:

    1. Pin set to sense high.
    2. Pin goes high, DETECT asserted, PORT event set.
    3. GPIOTE_IRQHandler called
    4. GPIOTE_IRQHandler clears PORT event.
    5. GPIOTE_IRQHandler reads IN register.
    6. GPIOTE_IRQHandler sets SENSE level according to value read from IN.

    If the pin goes low at any time after step 2 and before step 5, then the SENSE level will not be changed from its previous value and the DETECT signal will remain asserted, allowing no further PORT events to be generated.

    When multiple signals are being monitored the problem becomes only more likely, because any of them can change in this window and cause DETECT to remain asserted.

    What troubles me is that with no way to read the DETECT signal from the CPU, I cannot even readily see how this can be fixed. It seems like a fundamental flaw in the design.

Children
Related