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