Is there a simple example of how to enable two GPIOTE interrupts for fast timing?

I would like to monitor the states of switches sw0 and sw1 on my nrf7002dk board using GPIOTE interrupts.

Initially this is just for example purposes.

I eventually need to assign two other GPIO pins where I need to have the ability to time ~30 microsecond accuracy and I am finding that the standard Zephyr PORT interrupts not fast enough for my timing purposes.

The nrfx example that I looked at assigns just one GPIOTE pin and I am having difficulty assigning another pin. Specifically when it comes time to IRQ_CONNECT and nrfx_gpiote_in_init the second GPIO pin.

Thank you.

Parents
  • Hello,

    I've created an example that uses GPIOTE with 2 input pins. Please see attached. I may be able to expand this sample if you could describe what you are trying to monitor. Is it a signal input you want to measure on each pin as discussed in this thread:  GPIOTE + PPI SDK v2.5.2 issue + zephyr ? If so, you may need to use TIMER+PPI to accurately time signal periods.

    The Zephyr GPIO API also supports use IN events instead of PORT events if you change the triggering flag from *_LEVEL_* to _EDGE_ triggering. 

    Test sample

    hello_world_gpiote.zip

    Best regards,

    Vidar

  • Thank you Vidar.

    That example worked perfectly!

    To expand on this, I did try to use Zephyr GPIO _EDGE_ triggering in combination with the Zephyr timing functions in my pin event handlers (ie. timing_init, timing_start, timing_counter_get, timing_cycles_get), but found the values I was getting sometimes inconsistent as compared to the edge to edge times on my oscilloscope.

    Is this the wrong approach?

    Ultimately I would like to monitor the pulse widths on 4 GPIO pins where the widths can be as lows as 30 microseconds.

    Perhaps I need to move to timing with PPI. Is there an example of using PPI to get accurate GPIO edge to edge timing?

    Thanks again,

    Adam

  • Thanks Vidar,

    The clock_init function was missing from my code. I was not aware that had to be done. Thank you.

    I am able to produce a 135KHz reference frequency from the device that I am trying to monitor with my particular GPIO pin. That should give me a consistent high edge to low edge time of about 3.7 microseconds. I am capturing the first 100 cycles and printing them out in microseconds when my capturing has stopped.

    017 021 020 016 016 016 016 020 020 016
    016 020 020 016 016 016 021 020 016 016
    016 021 020 016 016 016 021 020 016 016
    016 021 020 016 016 016 021 020 016 016
    016 021 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016

    With the clock/timer example code that you gave me I am measuring much longer times and they are inconsistent. They are 4 to 5 times longer than I expected.

    I can change the timer config frequency and see the values increase by 2, 4, 8, etc. so I know the timer is working fine.

    For reference, I am NOT using the GPIOTE functions but rather the simpler GPIO setup functions (using GPIO_INT_EDGE_TO_ACTIVE and GPIO_INT_EDGE_TO_INACTIVE) and then capturing the high/low edge values of NRF_TIMER_CC_CHANNEL0 in my callback function and then storing the difference between the low/high edge times (the values I am printing out).

    Is using GPIO setup with callback not fast enough for this application? Is it possible that I am missing edges with this approach and the values I am measuring are actually 4 or 5 edges apart rather than every edge? Is there a way to prioritize the GPIO callback interrupt to get better performance?

    Another thing that makes me think I am missing edges is the number of edges that I am counting when I start and stop the measuring.

    uart:~$ start
    started: 1970/01/01 00:25:22 UTC
    uart:~$ stop
    stopped: 1970/01/01 00:25:24 UTC
    rising_edges: 57884
    falling_edges: 57883

    For a 135KHz signal I would have expected more like 270000 rising and falling edges in 2 seconds. The values are 4 to 5 times less than I would have expected.

    Thanks again.

    Adam

  • Hi Adam,

    The clock_init() function is not required, but it will make the TIMER run off the more accurate HFXO clock source instead of HFINT, which has a worst-case tolerance of +-8%.

    Interrupt latency is likely a factor for the poor measurements results. I recommend connecting the GPIOTE IRQ as a zero latency interrupt to see if that improves the timing:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/kernel/services/interrupts.html#zero-latency-interrupts 

    The other alternative is to disable the GPIOTE interrupt and instead poll the inputs using the gpio HAL (<hal/nrf_gpio.h>)

    Best regards,

    Vidar

  • Thank you Vidar,

    I switched to using GPIOTE and NRFX_GPIOTE_TRIGGER_LOTOHI for that pin and now I am seeing the correct number of low to high edges for 135KHz for roughly 2 seconds of counting.

    uart:~$ start
    started: 1970/01/01 00:00:09 UTC
    uart:~$ stop
    stopped: 1970/01/01 00:00:11 UTC
    rising_edges: 288872
    falling_edges: 0

    Clearly GPIO interrupts have too much latency.

    However, I need to capture LOTOHI and HITOLO edges and then read the timer on every interrupt. Is this also possible?

    If I change the trigger to NRFX_GPIOTE_TRIGGER_TOGGLE how will I know which edge is causing the interrupt? I suspect that calling nrfx_gpiote_in_is_set( pin ) in the pin event handler will not work.

    Best regards,

    Adam

  • Hi Adam,

    The latency will also increase as you start adding adding more signal inputs, so I think your best option may be to disable the GPIOTE interrupt after the first event. Then, use the GPIO HALs to read the pin states in a loop until the transfer is complete. The polling should happen outside of the interrupt context if you need to sample for longer periods to avoid blocking other parts of the app.

    Best regards,

    Vidar

  • Hi. I have similar problem, I try to do autobaud for my uart. I have to measure low level signal, so I need to use NRFX_GPIOTE_TRIGGER_TOGGLE. Is it possible to configure timer using PPI to start (capture) on falling edge and stop (capture) on rising?
    Best regards,
    PW  
         

Reply Children
Related