Reply
Posts: 54
Registered: ‎09-06-2017
Accepted Solution

GPIO Interrupt triggered by changing interrupt enable

When I change the enabled state of an interrupt it seems to trigger that interrupt. Example project attached for BGM111. In this example I am trying to change the interrupt configuration of PB1 when PB0 is pressed. When I press PB0 (pin 6), the configuration is changed, and this seems to trigger the interrupt for PB1 on pin 7, but only when disabling the interrupt. However, if the line of code disabling the interrupt is commented out, pin 7 interrupt is triggered when enabling the interrupt. This has left me thoroughly confused.

 

To reproduce:

Run program

Press and hold PB0

Observe pin 7 on the display. This has overwritten pin 6.

Release PB0

Observe pin 6 on the display (correct)

Press and hold PB0

Observe pin 6 on the display

Release

 

Comment out line 139 of main.c

Run

Press and hold PB0

Observe pin 6 on display

Release

Press and hold PB0

Observe pin 7 on display

Release

Posts: 563
Registered: ‎09-18-2015

Re: GPIO Interrupt triggered by changing interrupt enable

Hi @nickb,

 

There's a lot of code in your project. TL;DR.

 

Can you excerpt the relevant sections that show how you are changing the configuration?

 

John

Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

All the relevant code is in main.c. There is some abstraction in other files to make things simpler. Try running the project to reproduce the issue and commenting out lines 136 and 139 to see how that affects it.

 

 

void gecko_evt_system_boot(uint16 major, uint16 minor, uint16 patch, uint16 build, uint32 bootloader, uint16 hw) {
	graphInit("Hello\n");
	graphWriteString("world");

	/* Enable GPIO_EVEN interrupt vector in NVIC */
	NVIC_EnableIRQ(GPIO_ODD_IRQn);
	NVIC_EnableIRQ(GPIO_EVEN_IRQn);

	/* Configure PD8 interrupt on falling edge */
	GPIO_IntConfig(BUTTON_LED0_PORT, BUTTON_LED0_PIN, true, true, true);
	GPIO_IntConfig(BUTTON_LED1_PORT, BUTTON_LED1_PIN, true, true, true);
}

void gpio_interrupt(GPIO_Port_TypeDef port, int pin, bool state) {
	char output[30];
	snprintf(output, 22, "port %1u pin %2u state %1u", port, pin, state);
	graphWriteString(output);

	static bool states[4];
	if (port == BUTTON_LED0_PORT && pin == BUTTON_LED0_PIN) {
		if (!state) {
			if (states[1]) {
//				GPIO_PinModeSet(BUTTON_LED1_PORT, BUTTON_LED1_PIN, gpioModeInput, 0);
				GPIO_IntConfig(BUTTON_LED1_PORT, BUTTON_LED1_PIN, true, true, true);
//				GPIO_IntClear(1<<BUTTON_LED1_PIN);
			} else {
				GPIO_IntConfig(BUTTON_LED1_PORT, BUTTON_LED1_PIN, false, false, false);
//				GPIO_IntClear(1<<BUTTON_LED1_PIN);
//				GPIO_PinModeSet(BUTTON_LED1_PORT, BUTTON_LED1_PIN, gpioModePushPull, 0);
			}
			states[1] = !states[1];
		}
	}
}

 

 

 

Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

Attached a more basic project. Uses SWO for debug output. Output:

// first press
even: 00000000000000000000000001000000
odd: 00000000000000000000000010000000

// second press
even: 00000000000000000000000001000000

// third press
even: 00000000000000000000000001000000
odd: 00000000000000000000000010000000

// fourth press
even: 00000000000000000000000001000000
Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

Even clearer:

 

// 1
even: 00000000000000000000000001000000
Changing interrupt Off
odd:  00000000000000000000000010000000

// 2
even: 00000000000000000000000001000000
Changing interrupt On

// 3
even: 00000000000000000000000001000000
Changing interrupt Off
odd:  00000000000000000000000010000000

// 4
even: 00000000000000000000000001000000
Changing interrupt On
Posts: 563
Registered: ‎09-18-2015

Re: GPIO Interrupt triggered by changing interrupt enable

Hi @nickb,

 

I hate to be blunt, but that code is kind of a mess, and I'm not sure what you are intending to accomplish.

 

For now, can you explain why you are trying to interrupt on both rising and falling edges?

 

John

Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

Hi John,

 

Are you looking at the code from my last post? All I did was take the soc-empty template project and add just the code to exhibit the problem. The intended purpose of this is irrelevant, this just demonstrates a bug.

 

To make things clearer I've removed some stuff that isn't needed. Just look in main.c. I don't think I can simplify further.

 

The interrupt is configured only to trigger on falling edge to catch button down.

Posts: 563
Registered: ‎09-18-2015

Re: GPIO Interrupt triggered by changing interrupt enable

Hi @nickb,

 

If there a reason you are not simply disabling an interrupt like this?

 

GPIO_IntDisable(BUTTON_LED1_PIN);

GPIO_IntConfig() does quite a bit more than just turning the pin interrupt on or off, and I'm wondering if touching registers it configures while an interrupt is enabled might not result in unexpected behavior.

 

Change your code to make use of GPIO_IntDisable() and GPIO_IntEnable(), and see what happens.

 

There's some other things that are problematic in your configuration, but let's start with this for now.

 

John

Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

I was not aware of that function, and using that does work. There is still a bug here though I think. Narrowing it down, writing either the EXTIPSEL(L/H) or EXTIPINSEL(L/H) registers causes an interrupt when interrupts are enabled on that interrupt number.

 

A fix:

GPIO_ExtIntConfig, when the enable parameter is false, should disable the interrupt before changing the configuration, or just not change the configuration (although this could mislead the developer), or even just not have an enable parameter and either remove the enable statement and force the developer to use GPIO_IntDisable/GPIO_IntEnable or just enable it.

 

 

void GPIO_ExtIntConfig(GPIO_Port_TypeDef port,
                       unsigned int pin,
                       unsigned int intNo,
                       bool risingEdge,
                       bool fallingEdge,
                       bool enable)
{
  uint32_t tmp = 0;
#if !defined(_GPIO_EXTIPINSELL_MASK)
  (void)pin;
#endif

  EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
#if defined(_GPIO_EXTIPINSELL_MASK)
  EFM_ASSERT(GPIO_INTNO_PIN_VALID(intNo, pin));
#endif

  if(!enable) {
	/* Disable interrupt before changing parameters */
	BUS_RegBitWrite(&(GPIO->IEN), intNo, enable);
  }

  /* There are two registers controlling the interrupt configuration:
   * The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls
   * pins 8-15. */
  if (intNo < 8) {
    BUS_RegMaskedWrite(&GPIO->EXTIPSELL,
                       _GPIO_EXTIPSELL_EXTIPSEL0_MASK
                       << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo),
                       port << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo));
  } else {
    tmp = intNo - 8;
    BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
                       _GPIO_EXTIPSELH_EXTIPSEL8_MASK
                       << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp),
                       port << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
  }

#if defined(_GPIO_EXTIPINSELL_MASK)
  /* There are two registers controlling the interrupt/pin number mapping:
   * The EXTIPINSELL register controls interrupt 0-7 and EXTIPINSELH controls
   * interrupt 8-15. */
  if (intNo < 8) {
    BUS_RegMaskedWrite(&GPIO->EXTIPINSELL,
                       _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK
                       << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo),
                       ((pin % 4) & _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK)
                       << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo));
  } else {
    BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
                       _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK
                       << (_GPIO_EXTIPINSELH_EXTIPINSEL9_SHIFT * tmp),
                       ((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
                       << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
  }
#endif

  /* Enable/disable rising edge */
  BUS_RegBitWrite(&(GPIO->EXTIRISE), intNo, risingEdge);

  /* Enable/disable falling edge */
  BUS_RegBitWrite(&(GPIO->EXTIFALL), intNo, fallingEdge);

  /* Clear any pending interrupt */
  GPIO->IFC = 1 << intNo;

  if(enable) {
	/* Enable interrupt after changing parameters */
	BUS_RegBitWrite(&(GPIO->IEN), intNo, enable);
  }
}

 

Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

"There's some other things that are problematic in your configuration, but let's start with this for now."

 

What are the problems with the configuration?

Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

Actually this also applies if you are changing an enabled configuration, so the code should be:

void GPIO_ExtIntConfig(GPIO_Port_TypeDef port,
                       unsigned int pin,
                       unsigned int intNo,
                       bool risingEdge,
                       bool fallingEdge,
                       bool enable)
{
  uint32_t tmp = 0;
#if !defined(_GPIO_EXTIPINSELL_MASK)
  (void)pin;
#endif

  EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
#if defined(_GPIO_EXTIPINSELL_MASK)
  EFM_ASSERT(GPIO_INTNO_PIN_VALID(intNo, pin));
#endif
	
  /* Disable interrupt before changing parameters */
  BUS_RegBitWrite(&(GPIO->IEN), intNo, false);

  /* There are two registers controlling the interrupt configuration:
   * The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls
   * pins 8-15. */
  if (intNo < 8) {
    BUS_RegMaskedWrite(&GPIO->EXTIPSELL,
                       _GPIO_EXTIPSELL_EXTIPSEL0_MASK
                       << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo),
                       port << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo));
  } else {
    tmp = intNo - 8;
    BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
                       _GPIO_EXTIPSELH_EXTIPSEL8_MASK
                       << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp),
                       port << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
  }

#if defined(_GPIO_EXTIPINSELL_MASK)
  /* There are two registers controlling the interrupt/pin number mapping:
   * The EXTIPINSELL register controls interrupt 0-7 and EXTIPINSELH controls
   * interrupt 8-15. */
  if (intNo < 8) {
    BUS_RegMaskedWrite(&GPIO->EXTIPINSELL,
                       _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK
                       << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo),
                       ((pin % 4) & _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK)
                       << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo));
  } else {
    BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
                       _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK
                       << (_GPIO_EXTIPINSELH_EXTIPINSEL9_SHIFT * tmp),
                       ((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
                       << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
  }
#endif

  /* Enable/disable rising edge */
  BUS_RegBitWrite(&(GPIO->EXTIRISE), intNo, risingEdge);

  /* Enable/disable falling edge */
  BUS_RegBitWrite(&(GPIO->EXTIFALL), intNo, fallingEdge);

  /* Clear any pending interrupt */
  GPIO->IFC = 1 << intNo;

  if(enable) {
    /* Enable interrupt after changing parameters */
    BUS_RegBitWrite(&(GPIO->IEN), intNo, true);
  }
}
Posts: 563
Registered: ‎09-18-2015

Re: GPIO Interrupt triggered by changing interrupt enable

Hi @nickb,

 

What are the problems with the configuration?

 
Mechanical buttons are electrically bouncy.
 
If you are not going to implement a debounce loop, you should at least be using our input glitch filter:
 
GPIO_PinModeSet(BUTTON_LED1_PORT, BUTTON_LED1_PIN, gpioModeInputPullFilter, 1);
John
Posts: 54
Registered: ‎09-06-2017

Re: GPIO Interrupt triggered by changing interrupt enable

I had set 'Filter' to enabled in hardware configurator, but that doesn't seem to output anything in InitDevice. Does filter not work and I should use the 'filter' pin mode instead?