Using 3buttons and 5way switch with interrupts

Hi,

For my project I want to use the 3 top buttons and the 5 way switch at the same time. Therefor I am attaching interrupts to all possible inputs:

  /***** top buttons *****/
  pinMode(WIO_KEY_A, INPUT_PULLUP);
  pinMode(WIO_KEY_B, INPUT_PULLUP);
  pinMode(WIO_KEY_C, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(WIO_KEY_A), buttonA, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_KEY_B), buttonB, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_KEY_C), buttonC, FALLING);

  /***** 5 way switch *****/
  pinMode(WIO_5S_UP, INPUT_PULLUP);
  pinMode(WIO_5S_DOWN, INPUT_PULLUP);
  pinMode(WIO_5S_LEFT, INPUT_PULLUP);
  pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
  pinMode(WIO_5S_PRESS, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(WIO_5S_UP),    switchUP, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_DOWN),  switchDOWN, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_LEFT),    switchLEFT, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_RIGHT),  switchRIGHT, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_PRESS), switchPRESS, FALLING);

If I only do the top buttons, all buttons work as expected,
if I only do the 5way switch, all actions work as expected,
but if I attach all 8 interrupts, the 5way switch UP button and KEY_C seem to conflict.
It looks like only the interrupt that I attach first works.

I tried a work around, where I detach the interrupts of the top buttons when the 5way button is pressed and only then attach the other interrupts of the 5way switch,
but even after detaching the interrupt of KEY_C, I can not get the 5S_UP to work.

Does somebody else has seen this issue before?
Am I doing something wrong? Or is what I am trying to do impossible?

Thanks for all the help,
Sander

Hi Sander, I confirm your observations.

This issue can be seen in a simpler situation (making it perhaps worse than you expected?). Don’t attach interrupts to any other buttons/keys except for A and UP. No interrupt will be generated from either button, assuming single/isolated button presses.

If you don’t attach an interrupt to A, the remaining seven buttons (including UP) generate interrupts as expected - any or all of them work just fine via interrupts. Similarly, if you don’t attach to UP, the remaining seven buttons again work as expected. It’s calling attachInterrupt() on both A and UP that seems to be the core offender.

Here’s a slightly more complete list of anomalous behaviors when both A and UP have interrupts attached. Some of these relate to multi-button presses.

  • If interrupts are attached to both A and UP:
    • Pressing A alone won’t trigger an interrupt
    • Pressing A while UP is held WILL trigger an interrupt
    • Pressing UP alone won’t trigger an interrupt
    • Pressing UP while A is held WILL trigger an interrupt

I haven’t thoroughly reviewed the WIO schematics, pin usage, and associated constraints of the SAMD5x, but bottom line is that I haven’t yet found a way to enable A and UP simultaneously and get expected interrupt behavior. And perhaps a later rev of the WIO Terminal has corrected this.

–Don

I got motivated to dig deeper and trace the hardware. With Seeed schematics and SAMD51 manual in hand, I think I found the issue - or at least a strong lead to pursue further.

Buttons A and UP both map to the same external interrupt. The interrupts in use for the 8 buttons are: 3,4,5,7,10,11,12. That’s 7 unique interrupts, not 8. Two buttons share the “EXTINT[10]” interrupt - and those appear to be A (aka “BUTTON1”) and UP (aka “SWITCH_U”).

There is no way to remap these interrupt assignments via software configuration, AFAIK. ​I need to study this a bit more to confirm the above. I checked this on Rev 1.0, 1.1 and 1.2 schematics, with the same issue found in all three. This may be something for Seeed to consider in Rev 1.3.

–Don

Ok, this doesn’t explain lack of interrupt generation. All buttons could be on one interrupt and we should get reliable interrupt generation (just a bit more to sort out in the ISR afterwards). Perhaps the external interrupt controller (EXTINT) isn’t the culprit, or even in use.

I took a look at the external pullups for the buttons in the schematic — buttons ABC have stronger pullups (4.7k) while the 5-way/UDLRP buttons have weaker pullups (100k). So whether you use INPUT or INPUT_PULLUP for pinMode, an interrupt will still be generated. I confirmed this for the other buttons.

Does anyone know how to interpret the interrupt number returned by digitalPinToInterrupt()? For these button pins, the interrupt numbers are all unique and do not match EXTINT intterupt numbers. I have a feeling an interrupt vector table is in use that I’m not seeing.

Hi dspdon,

Thanks for all the help.
I also noticed that detaching the interrupt does not work.
I have a test where at the setup only A is attached to the interrupt but UP is not.
Now the interrupt for button A works, but if I than detach the interrupt from A and attach the interrupt to UP (by using button PRESS as the switch), the interrupt does not work anymore.
Switching again (so detaching from UP and attaching A) does not bring button A back.

So after detaching A and attaching UP, you need to press both buttons in order to trigger the interrupt.

bool AisActive = true;

void switchUP() {
  Serial.println("5 Way Int Up");
}
void switchPRESS() {
  if (AisActive) {
    detachInterrupt(digitalPinToInterrupt(WIO_KEY_A));
    attachInterrupt(digitalPinToInterrupt(WIO_5S_UP), switchUP, FALLING);
    AisActive = false;
    Serial.println("A is not active");
  } else {
    detachInterrupt(digitalPinToInterrupt(WIO_5S_UP));
    attachInterrupt(digitalPinToInterrupt(WIO_KEY_A), buttonA, FALLING);
    AisActive = true;
    Serial.println("A is active");
  }
}
void buttonA() {
  Serial.println("Button Int A");
}

void setup() {
  Serial.begin(9600);
  pinMode(WIO_5S_UP, INPUT_PULLUP);
  pinMode(WIO_5S_PRESS, INPUT_PULLUP);
  pinMode(WIO_KEY_A, INPUT_PULLUP);

  AisActive = true;
  attachInterrupt(digitalPinToInterrupt(WIO_KEY_A), buttonA, FALLING);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_PRESS), switchPRESS, FALLING);
}

Some news on this: the SAMD51 has a constraint on the external interrupt controller (EIC). Only one interrupt will be active when more than one pin/peripheral shares the same EXTINT:

Document: https://files.seeedstudio.com/wiki/Wio-Terminal/res/ATSAMD51.pdf
Sec 23.6.6, Note 2:
“If an external interrupts (EXTINT) is common on two or more I/O pins, only one will be active (the first one programmed).”

Armed with this insight, I could then find related discussion on other forums that confirm the constraint:



etc

There are Arduino-based button handler libraries available that offer callback functions as the main user API, even though they use polling-based approaches in their implementation. These offer a programming paradigm that may give you some of what you want: an ability to associate a function with a button action, e.g., call this function when button C has a double-click, call that function when UP is released, etc. They just don’t use pin-based interrupts for the buttons in their implementation. They need something running in loop(), or something attached to a timer that does the polling.