NCS v2.6.0: Instructions to use GPIOTE and Zephyr GPIO API

Hello All,

This is an instructional post that answers the following question with explanations and code samples: "How do I use GPIOTE while also using Zephyr to control the GPIOs?"

I am making this post because there is a LOT of confusion on Devzone about this and there isn't a ton of easily available documentation on this one. I hope this helps you!

TL;DR:

The Zephyr GPIO APIs use GPIOTE to setup interrupts. Therefore, when using GPIOTE NRFX drivers, you DO NOT NEED to set up input GPIOTEs if you have already configured interrupts for those pins via the Zephyr GPIO API (gpio_pin_interrupt_configure_dt()). You DO NEED to configure the output GPIOTEs because Zephyr does not use GPIOTEs for outputs. However, you may/may not (haven't tested) be able to use Zephyr to set up the pin as an output, but why would you need to anyways if it's controlled via PPI? 

Explanation:

Starting from a Nordic NRFX GPIOTE example such as the following:

https://github.com/nrfconnect/sdk-zephyr/blob/main/samples/boards/nrf/nrfx/src/main.c

You cannot add in any of Zephyr's GPIO API etc. However, this is not actually an issue since 2021 (https://github.com/nrfconnect/sdk-zephyr/pull/432), and a simple workaround exists that should be worked into SOP eventually I should hope.

1: The IRQ_CONNECT()

If you try to set up a pin using Zephyr as an input with an interrupt, you can no longer set up the GPIOTEs like nordic examples show in their examples. This is because the Zephyr drivers that control GPIOs use the GPIOTEs in some cases. In particular, if you set up and input with an interrupt like this:

gpio_pin_configure_dt(&my_gpio, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&my_gpio, GPIO_INT_EDGE_FALLING);

Zephyr will use a GPIOTE channel to set up the interrupt for the GPIO pin. This means that if you try to do this:

IRQ_CONNECT( <stuff for connecting GPIOTE interrupt> );

It's going to give you an error. If you check your isr_tables.c you will see that both try to put an interrupt in the same position (for me it is position 6, as defined in the device tree)! This is okay! All this means is that the interrupt is ALREADY set up for the GPIOTE device. Therefore, DO NOT use IRQ_CONNECT(gpiote stuff) if you plan on using gpio_pin_interrupt_configure_dt().

2. The NRFX GPIOTE Functions

nrfx_gpiote_init()

Do not use this function for the same reason you shouldn't use IRQ_CONNECT.

nrfx_gpiote_channel_alloc(..., input_channel);

Do not use this for inputs! For output GPIOs that you want to control with GPIOTE you still need to do this. Something like "nrfx_gpiote_channel_alloc(&gpiote_inst, &out_channel)".

nrfx_gpiote_input_configure()

Do not use for the same reason you shouldn't use IRQ_CONNECT! Remember, this applies because Zephyr has already initialized your input pin using GPIOTE.

nrfx_gpiote_trigger_enable(&gpiote_inst, INPUT_PIN, true)

Do not use! GPIOTE input already set up by zephyr.

3. How do I connect it to PPI if I don't control the input with GPIOTE?

You can still do this like normal! Given that you've used Zephyr APIs to set up an interrupt on a particular pin, use the following to set up endpoints for that pin:

nrfx_gppi_channel_endpoints_setup(ppi_channel,
    nrfx_gpiote_in_event_address_get(&gpiote, INPUT_PIN), //INPUT_PIN = raw pin number. 
    nrfx_gpiote_out_task_address_get(&gpiote, OUTPUT_PIN)) //OUTPUT_PIN = raw pin number.
//Raw pin numbers are like this: P1.15 = 32+15 or P0.12 = 12. 
// Maybe there is a convenient way to get this from the device tree?

4. Example programs

Please reference the following two programs.

This first one is the Nordic provided program that shows how to connect a GPIOTE input to a GPIOTE output with PPI (NCS v2.6.0)

https://github.com/nrfconnect/sdk-zephyr/blob/main/samples/boards/nrf/nrfx/src/main.c

This one is the above example but I modified it to work with Zephyr APIs. I left the program there, but I commented out the parts that shouldn't be used. There should be enough information between the two projects to be able to figure out how to do this. After doing this, I was also able to use the NRFX EGU endpoints and NRFX TIMER endpoints with the PPI.

https://github.com/NathanGregKidd/gpiote_with_zephyr/tree/main

God Bless,

Nathan

Parents
  • Just wanted to add a few details that I left out:

    Device Tree Overlay Fragment

    You should add the following fragment to you device tree. This is because there are TWO different GPIO interrupt mechanisms (at least on the nRF52840!). One is through the PORT event and one is through the IN event. The IN event is what we need and actually requires a different hardware "path". But don't worry! it's not difficult! Just add the below to your overlay and change the following to your needs:

    gpio0: gpio@50000000

    OR

    gpio1: gpio@50000300 

    (find the gpio bank in your device tree and use that)

    and also change the sense-edge-mask value to select the correct pins! I think you need to mask the bit position of the pin as 0.

    soc 
        {
            gpio0: gpio@50000000 {                                          
                sense-edge-mask = < 0xffff3fff >; Use a bit mask for the needed pins!!                       
            };
        }; 

    As an aside, I'm not sure if the project I linked to that I made includes this (I deleted my device tree except for the minimum right before commiting).

    Prj.conf

    https://github.com/nrfconnect/sdk-zephyr/commit/a86ac1f8c2a48e32ed824f0f5ea49d45ceaa345f

    It seems to indicate that the config 

    CONFIG_GPIO_NRFX=y 

    is necessary, but I'm not sure. You may want to try this in lieu of or in addition to

    CONFIG_GPIO=y

Reply
  • Just wanted to add a few details that I left out:

    Device Tree Overlay Fragment

    You should add the following fragment to you device tree. This is because there are TWO different GPIO interrupt mechanisms (at least on the nRF52840!). One is through the PORT event and one is through the IN event. The IN event is what we need and actually requires a different hardware "path". But don't worry! it's not difficult! Just add the below to your overlay and change the following to your needs:

    gpio0: gpio@50000000

    OR

    gpio1: gpio@50000300 

    (find the gpio bank in your device tree and use that)

    and also change the sense-edge-mask value to select the correct pins! I think you need to mask the bit position of the pin as 0.

    soc 
        {
            gpio0: gpio@50000000 {                                          
                sense-edge-mask = < 0xffff3fff >; Use a bit mask for the needed pins!!                       
            };
        }; 

    As an aside, I'm not sure if the project I linked to that I made includes this (I deleted my device tree except for the minimum right before commiting).

    Prj.conf

    https://github.com/nrfconnect/sdk-zephyr/commit/a86ac1f8c2a48e32ed824f0f5ea49d45ceaa345f

    It seems to indicate that the config 

    CONFIG_GPIO_NRFX=y 

    is necessary, but I'm not sure. You may want to try this in lieu of or in addition to

    CONFIG_GPIO=y

Children
No Data
Related