Need help with nRF5340 IRQ conflict errors when adding GPIOTE drivers to Nordic Bluetooth Peripheral UART project

We are using the Nordic Bluetooth "Peripheral UART" project as a base for our project and I am attempting to add the GPIOTE drivers to the project but getting the following error:

gen_isr_tables.py: error: multiple registrations at table_index 47 for irq 47 (0x2f)
Existing handler 0x39aa5, new handler 0x39aa5
Has IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?

It looks like there is an IRQ conflict with adding the GPIOTE drivers and the DK drivers.

The reason I need the GPIOTE driver is in order to add multiple QDEC rotary encoders (which is not supported by the current QDEC driver).  I have attached a sample app that demonstrates the error.  We are using NCS v2.3.0 targeting nrf5340dk_nrf5340_cpuapp_ns.  We need to deconflict the IRQ but also keep the as much of the DK drivers as possible to expedite this project.

See attached application below:
peripheral_uart_GPIOTE.zip

Thanks for the help!

Parents
  • I understand.

    You cannot use nrfx_gpiote driver separately when you are using gpio_nrfx.c.

    You need to do what nrf\lib\dk_buttons_and_leds\dk_buttons_and_leds.c did. You need to get the device pointer of gpiote zephyr driver like below

    const struct device *const gpiote = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiote));

    And use this instead of direct nrfx API for GPIOTE. 

    You can register another callback for your application just like nrf\lib\dk_buttons_and_leds\dk_buttons_and_leds.c did.

  • The file dk_buttons_and_leds.c is using loads of macros to abstract away the code to support multiple DKs so referencing the DK code doesn't help me much because I am very unfamiliar with Zephyr and Nordic.

    When you say one CANNOT use GPIO and GPIOTE I assume you mean that it is forbidden to have the following in the prj.conf:
    CONFIG_GPIO=y
    CONFIG_NRFX_GPIOTE=y

    Can you modify my posted application to trigger an event on the rising edge of GPIO pin 37?  I understand you may not be able to test but I would like to see what the code would look like to do this.

    As a side note I have been able to get the generic nrfx GPIOTE sample application working on the nRF5340 even though the 5340 is not listed as supported by the sample application.  However, this application is using no GPIO.

     

  • Thank you for the response and status update for support.  We are using the NRFX GPIOTE driver.  The problem we are solving is having more than one QDEC.  The QDEC driver works for one rotary encoder but not more than one currently.

    Here are our includes:

    #include "nrfx_qdec.h"
    #include <nrfx_gpiote.h>

    I am able to get the absolute pin number by using the following macros:

    //#define INPUT_PIN	          DT_GPIO_PIN(DT_ALIAS(sw0), gpios)
    #define GPIO_ENC_LEFT_A     NRF_GPIO_PIN_MAP(DT_PROP(DT_GPIO_CTLR(DT_NODELABEL(encoder0), gpios), port), DT_GPIO_PIN(DT_NODELABEL(encoder0), gpios))
    #define GPIO_ENC_LEFT_B     NRF_GPIO_PIN_MAP(DT_PROP(DT_GPIO_CTLR(DT_NODELABEL(encoder1), gpios), port), DT_GPIO_PIN(DT_NODELABEL(encoder1), gpios))
    #define GPIO_ENC_RIGHT_A    NRF_GPIO_PIN_MAP(DT_PROP(DT_GPIO_CTLR(DT_NODELABEL(encoder1), gpios), port), DT_GPIO_PIN(DT_NODELABEL(button2), gpios))
    #define GPIO_ENC_RIGHT_B    NRF_GPIO_PIN_MAP(DT_PROP(DT_GPIO_CTLR(DT_NODELABEL(encoder3), gpios), port), DT_GPIO_PIN(DT_NODELABEL(encoder3), gpios))

    I tested the absolute pin address using one of the existing buttons on GPIO1 and it worked fine.

    The new issue I am having is that our rotary encoder has its own pull-ups and filtering on the A and B "sensors" or "contacts" (encoder is electro-mechanical).  The problem is that it seems like GPIO and/or GPIOTE pins are forced to have pull-ups or pull-downs declared in the device map and that causes a voltage divider with the hardware encoder.  Thus, we do not see valid edges when turning the encoder.

    Below is my current device map:

    	encoders {
    		compatible = "gpio-keys";
    		encoder0: encoder_0 {
    			gpios = <&gpio1 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    			label = "Encoder Left A";
    		};
    		encoder1: encoder_1 {
    			gpios = <&gpio1 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    			label = "Encdoer Left B";
    		};
    		encoder2: encoder_2 {
    			gpios = <&gpio1 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    			label = "Encoder Right A";
    		};
    		encoder3: encoder_3 {
    			gpios = <&gpio1 6 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    			label = "Encdoer Right B";
    		};
    

    Is there a method to declare GPIO pins w/o having any pull set?

    I also see some GPIOTE configuration in the code as well:

    	static const nrfx_gpiote_input_config_t input_config = {
    		.pull = NRF_GPIO_PIN_NOPULL,
    	};
    	const nrfx_gpiote_trigger_config_t trigger_config = {
    		.trigger = NRFX_GPIOTE_TRIGGER_TOGGLE,
    		.p_in_channel = &in_channel,
    	};
    	static const nrfx_gpiote_handler_config_t handler_config = {
    		.handler = encoder_handler,
    	};
    	err = nrfx_gpiote_input_configure(GPIO_ENC_RIGHT_A,
    					  &input_config,
    					  &trigger_config,
    					  &handler_config);
    	LOG_INF("nrfx_gpiote_input_configure pin=%u", GPIO_ENC_RIGHT_A);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_input_configure error: 0x%08X", err);
    		return;
    	}

    With those settings I am still not seeing edges on when I rotate the rotary encoder.

  • TrulliDev said:
    The problem we are solving is having more than one QDEC.  The QDEC driver works for one rotary encoder but not more than one currently.

    This seems strange. Did you investigate the qdec_nrfx driver or something else? I am not familiar with any of them, but I would assume expect that a decoder driver should support multiple instances, since it is quite common to use multiple decoders.

    I am just asking to see if you want to double check on this point. I could help if you wish to. If you are fully invested in the nrfx solution, let's just forget about it.

    TrulliDev said:
    The problem is that it seems like GPIO and/or GPIOTE pins are forced to have pull-ups or pull-downs declared in the device map

    This is not right. You can simply remove the pull flag from its declaration, and it would mean no pull.

    However, the nrfx driver controls the pin directly. So regardless of how the pins are defined in the DTS, the nrfx driver should be able to override it.

    However, unless you are using the Zephyr driver somewhere, the DTS definition should have no effect, and the nrfx driver should have the final say.

    In the case of two drivers both configuring one pin, then I cannot be sure of the behavior without investigating. However, I would guess with some confidence that the API that is called later will have the final effect.

    TrulliDev said:
    Is there a method to declare GPIO pins w/o having any pull set?

    The code you included should do exactly that.

    How did you check if the pull configuration is set correctly or not?

    Here is how I setup a simple test where I both set the pull configuration using nrfx Driver API and confirm its effect with nrfx HAL API:

    #include <nrfx_gpiote.h>
    
    void print_pull(uint8_t port, uint8_t pin)
    {
    	nrf_gpio_pin_pull_t t = nrf_gpio_pin_pull_get(NRF_GPIO_PIN_MAP(port, pin));
    	printk("P%d.%02d ", port, pin);
    	switch (t)
    	{
    		case NRF_GPIO_PIN_NOPULL: 	printk("NRF_GPIO_PIN_NOPULL"); break;
    		case NRF_GPIO_PIN_PULLDOWN: printk("NRF_GPIO_PIN_PULLDOWN"); break;
    		case NRF_GPIO_PIN_PULLUP: 	printk("NRF_GPIO_PIN_PULLUP"); break;
    		default: printk("DEADBEEF");
    	}
    	printk("\n");
    }
    
    void nrfx_configure(uint8_t port, uint8_t pin, const nrf_gpio_pin_pull_t pull)
    {
    	uint32_t err;
    	const nrfx_gpiote_input_config_t input_config = {
    		.pull = pull,
    	};
    	err = nrfx_gpiote_input_configure(NRF_GPIO_PIN_MAP(port, pin),
    					  &input_config,
    					  NULL,
    					  NULL);
    	printk("nrfx_gpiote_input_configure %d\n", err);
    }
    
    void main(void)
    {
    	nrfx_configure(0, 14, NRF_GPIO_PIN_PULLUP);
    	nrfx_configure(0, 15, NRF_GPIO_PIN_PULLDOWN);
    
    	print_pull(0, 14);
    	print_pull(0, 15);
    }

    With this test, I can see that the pins are configured correctly no matter how the pins are defined in my DTS. That is including undefined, or defined with completely different pull, or defined with no pull.


    What happened when you change the pull configuration in your DTS, for example, like this?

    		encoder0: encoder_0 {
    			gpios = <&gpio1 10 (GPIO_ACTIVE_LOW)>;
    			label = "Encoder Left A";
    		};

    The expectation is that it should not have any effect. If it does, then you are having the Zephyr driver running.

  • Hieu,

    If you look at nrfx_qdec.h you will see that they hard coded the functions for "QDEC0" so they are only able to take one instance of a QDEC.  It is my impression that the driver could be extended to support more than one QDEC but it would have to be taken out-of-tree.  We are also having a different issue with the QDEC driver that causes us to get too many interrupts per rotation.  For those reasons we are attempting to roll our own driver.

    Regarding the pin configuration, the hardware is in an enclosure that prevents me from easily probing the circuit.  I will remove the hardware and see if I can characterize what is going on.

    Regards,
    Peter

  • Peter,

    Thanks for the information. I will wait for your update. Susheel will be back this week so either he or I will continue supporting you.

    Regards,

    Hieu

  • Hello,

    I was able to scope my pins and they do appear to have the proper voltage.  We are using a CR 2032 button cell battery at 3V and I am seeing the A and B signals swing between 0V and 3V as expected.

    What I discovered is that I was missing a call that would enable the trigger for my sensor pins.

    	// Enable encoder sensor trigger events
    	nrfx_gpiote_trigger_enable(GPIO_ENC_RIGHT_A, true);
    	nrfx_gpiote_trigger_enable(GPIO_ENC_RIGHT_B, true);

    After adding the above code I finally started to get the trigger events for "GPIO_ENC_RIGHT_A" but now I have a new problem.  I am not getting trigger events for "GPIO_ENC_RIGHT_B" pin.  When it is all said and done I have two rotary encoders each with an A and B sensor pin so I need a total of four triggers to fire.

    Is there some limitation of GPIOTE to lmiit only to one trigger or how do I enable up to four triggers at once?

    Thanks,
    Peter

Reply
  • Hello,

    I was able to scope my pins and they do appear to have the proper voltage.  We are using a CR 2032 button cell battery at 3V and I am seeing the A and B signals swing between 0V and 3V as expected.

    What I discovered is that I was missing a call that would enable the trigger for my sensor pins.

    	// Enable encoder sensor trigger events
    	nrfx_gpiote_trigger_enable(GPIO_ENC_RIGHT_A, true);
    	nrfx_gpiote_trigger_enable(GPIO_ENC_RIGHT_B, true);

    After adding the above code I finally started to get the trigger events for "GPIO_ENC_RIGHT_A" but now I have a new problem.  I am not getting trigger events for "GPIO_ENC_RIGHT_B" pin.  When it is all said and done I have two rotary encoders each with an A and B sensor pin so I need a total of four triggers to fire.

    Is there some limitation of GPIOTE to lmiit only to one trigger or how do I enable up to four triggers at once?

    Thanks,
    Peter

Children
  • Hi,

    GPIOTE is limited, but, on the nRF5340, the limit is eight event or task total. So, you should be able to get four triggers.

    I just tested the following code. In writing it, I realize that maybe you are reusing GPIOTE channel for all inputs? Here is my code, tested to be working with nRF DK buttons.

    #define PRINTK_CASE(_CASE) case _CASE: printk(#_CASE); break
    
    void gpiote_interrupt_handler_230504(nrfx_gpiote_pin_t     pin,
    										nrfx_gpiote_trigger_t trigger,
    										void *                p_context)
    {
    	printk("gpiote_interrupt_handler_230504 Pin %d ", pin);
    	switch (trigger)
    	{
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_NONE);
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_LOTOHI);
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_HITOLO);
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_TOGGLE);
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_LOW);
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_HIGH);
    		PRINTK_CASE(NRFX_GPIOTE_TRIGGER_MAX);
    		default: printk("OH NO");
    	}
    	printk("\n");
    }
    
    // Interrupt enabled on 
    void config_input_via_nrfx_230504(const uint8_t port, const uint8_t pin)
    {
    	nrfx_err_t err;
    
    	nrfx_gpiote_input_config_t t_input_config = {.pull = NRF_GPIO_PIN_PULLUP};
    	
    	uint8_t t_gpiote_channel;
    	err = nrfx_gpiote_channel_alloc(&t_gpiote_channel);
    	printk("nrfx_gpiote_channel_alloc ");
    	switch (err)
    	{
    		PRINTK_CASE(NRFX_SUCCESS);
    		PRINTK_CASE(NRFX_ERROR_INTERNAL);
    		PRINTK_CASE(NRFX_ERROR_NO_MEM);
    		PRINTK_CASE(NRFX_ERROR_NOT_SUPPORTED);
    		PRINTK_CASE(NRFX_ERROR_INVALID_PARAM);
    		PRINTK_CASE(NRFX_ERROR_INVALID_STATE);
    		PRINTK_CASE(NRFX_ERROR_INVALID_LENGTH);
    		PRINTK_CASE(NRFX_ERROR_TIMEOUT);
    		PRINTK_CASE(NRFX_ERROR_FORBIDDEN);
    		PRINTK_CASE(NRFX_ERROR_NULL);
    		PRINTK_CASE(NRFX_ERROR_INVALID_ADDR);
    		PRINTK_CASE(NRFX_ERROR_BUSY);
    		PRINTK_CASE(NRFX_ERROR_ALREADY_INITIALIZED);
    		PRINTK_CASE(NRFX_ERROR_DRV_TWI_ERR_OVERRUN);
    		PRINTK_CASE(NRFX_ERROR_DRV_TWI_ERR_ANACK);
    		PRINTK_CASE(NRFX_ERROR_DRV_TWI_ERR_DNACK);
    		default: printk("OH NO");
    	}
    	printk("\n");
    
    	nrfx_gpiote_trigger_config_t t_trigger_config = {
    		.trigger 		= NRFX_GPIOTE_TRIGGER_TOGGLE, 
    		.p_in_channel 	= &t_gpiote_channel
    	};
    
    	nrfx_gpiote_handler_config_t t_handle_config = {
    		.handler 	= &gpiote_interrupt_handler_230504,
    		.p_context 	= NULL
    	};
    	
    	err = nrfx_gpiote_input_configure(pin, &t_input_config, &t_trigger_config, &t_handle_config);
    	printk("nrfx_gpiote_input_configure ");
    	switch (err)
    	{
    		PRINTK_CASE(NRFX_SUCCESS);
    		PRINTK_CASE(NRFX_ERROR_INTERNAL);
    		PRINTK_CASE(NRFX_ERROR_NO_MEM);
    		PRINTK_CASE(NRFX_ERROR_NOT_SUPPORTED);
    		PRINTK_CASE(NRFX_ERROR_INVALID_PARAM);
    		PRINTK_CASE(NRFX_ERROR_INVALID_STATE);
    		PRINTK_CASE(NRFX_ERROR_INVALID_LENGTH);
    		PRINTK_CASE(NRFX_ERROR_TIMEOUT);
    		PRINTK_CASE(NRFX_ERROR_FORBIDDEN);
    		PRINTK_CASE(NRFX_ERROR_NULL);
    		PRINTK_CASE(NRFX_ERROR_INVALID_ADDR);
    		PRINTK_CASE(NRFX_ERROR_BUSY);
    		PRINTK_CASE(NRFX_ERROR_ALREADY_INITIALIZED);
    		PRINTK_CASE(NRFX_ERROR_DRV_TWI_ERR_OVERRUN);
    		PRINTK_CASE(NRFX_ERROR_DRV_TWI_ERR_ANACK);
    		PRINTK_CASE(NRFX_ERROR_DRV_TWI_ERR_DNACK);
    		default: printk("OH NO");
    	}
    	printk("\n");
    
    	nrfx_gpiote_trigger_enable(pin, true);
    }

    Regards,

    Hieu

  • Hieu,

    I am able to get the GPIOTE triggering on all four of my "pins" now.  I am using four "channels" and that is working fine.  I have all four triggers calling the same event handler.  We still have some work to do in order to de-glitch the rotary encoder signals but that is outside of the GPIOTE driver layer.

    Right now I am triggering on edge toggle, but I might have a need to trigger on both rising and falling edge in a way that I can decode if the trigger was a rising or falling edge in the event handler.  Toggle always returns toggle trigger regardless of the edge direction.  I assume I cannot just OR together the LOTOHI and HITOLO trigger events because it is an enum and not a bitmask?

    Thanks for the help,
    Peter

  • Peter,

    For detecting what edge it is, I think you can use the GPIO (not GPIOTE) HAL APIs to read the pin state.

    Please also be informed that I am out of office from today, for three working days.

    Regards,

    Hieu

Related