Spurious interrupts with three gen3 GPIO pins

In reading the docs I’ve only noticed a limit on the number of pins that can be assigned to interrupts with gen3 devices. With both Boron and Argon I have an app that’s only using interrupts with four pins: one sensor and three push buttons. Yet when I press button number three I frequently get button 2 and/or button 1 interrupts too, and pressing button 2 less frequently gives me button 1 or button 3 interrupts, while button 1 never gives anything other than a button 1 interrupt.

Am I just unlucky with picking pins? Are there restrictions on specific pins that I’ve missed?

The code below has more details and reproduces this problem. It just takes a few pushes of button three with breakpoints set in one and two to see the behavior without any LEDs, and I suspect a switch with no hardware debouncing might work just as well or better than the ones I’m using.

/*
 This code reproduces a bug in DeviceOS causing spurious button 1 and button 2 interrupts when
 button 3 is pressed with a high probability. Pressing button 2 multiple times causes spurious
 interrupts for the button 1 and 3 handlers with a lower probability. Ppessing button 1 any number
 of times (up to perhaps 100) caused no  spurious calls of the button 2 or button 3 interrupt
 handlers.

 Reproduced with DeviceOS version 2.1 and 3.1 on an Argon

 Button circuit: For each button a normally open momentary switch connects the pin to ground. A 100nF cap
 across the switch and external pullup resistor between the pin and 3.3 volts arrange to prevent debounce,
 taking about one microsecond to settle down to ground potential with no exursions back to logic high
 with the switches used (6mm square tactile).

 The LEDs are just connected to each pin with 3K ohm series resistors to ground.
*/

#pragma GCC optimize ("O0")

#include "Particle.h"
SYSTEM_MODE(MANUAL) /*!< Do not check in with mother ship before executing setup() */
SYSTEM_THREAD(ENABLED)

static const uint8_t USER_BUTTON_1_PIN = 9;
static const uint8_t USER_BUTTON_2_PIN = 1;
static const uint8_t USER_BUTTON_3_PIN = 3;

static const uint8_t RED_LED_PIN = 4;
static const uint8_t YELLOW_LED_PIN = 5;
static const uint8_t GREEN_LED_PIN = 6;

volatile bool user_button_1_pressed;
volatile bool user_button_2_pressed;
volatile bool user_button_3_pressed;

void button1() {
  user_button_1_pressed = true;
}

void button2() {
  user_button_2_pressed = true;
}

void button3() {
  user_button_3_pressed = true;
}

void setup() {
    pinMode(USER_BUTTON_1_PIN, INPUT); // External pullups: see circuit description above
    pinMode(USER_BUTTON_2_PIN, INPUT);
    pinMode(USER_BUTTON_3_PIN, INPUT);
    pinMode(RED_LED_PIN, OUTPUT);
    pinMode(YELLOW_LED_PIN, OUTPUT);
    pinMode(GREEN_LED_PIN, OUTPUT);
    attachInterrupt(USER_BUTTON_1_PIN, button1, FALLING);
    attachInterrupt(USER_BUTTON_2_PIN, button2, FALLING);
    attachInterrupt(USER_BUTTON_3_PIN, button3, FALLING);
}

void loop() {
  static bool led_1, led_2, led_3;

  if (user_button_1_pressed) {
    user_button_1_pressed = false;
    if (led_1) {
      led_1 = false;
      digitalWrite(RED_LED_PIN, LOW);
    } else {
      led_1 = true;
      digitalWrite(RED_LED_PIN, HIGH);
    }
  }

  if (user_button_2_pressed) {
    user_button_2_pressed = false;
    if (led_2) {
      led_2 = false;
      digitalWrite(YELLOW_LED_PIN, LOW);
    } else {
      led_2 = true;
      digitalWrite(YELLOW_LED_PIN, HIGH);
    }
  }

  if (user_button_3_pressed) {
    user_button_3_pressed = false;
    if (led_3) {
      led_3 = false;
      digitalWrite(GREEN_LED_PIN, LOW);
    } else {
      led_3 = true;
      digitalWrite(GREEN_LED_PIN, HIGH);
    }
  }

  delay(1);
}

I should add the comment says “reproduced on an Argon” but my app exhibits this behavior the same on a Boron too…

@petesoper, the input mode you selected for your button pins (INPUT) will leave the pins “floating” and susceptible to stray capacitance, say from your finger! Since you are using a FALLING interrupt, meaning your buttons connect to GND, I suggest you use INPUT_PULLUP for you pinMode(), making sure the pins are tied to 3.3v internally via a weak (13K ohm) resistor. Try that to see if it helps.

I’m using 3k pullups outboard, as described in the comments.

I think your 100nF cap is way too small to effectively debounce the switch. What happens if you switch it to a larger value like 5 or 10uF?

If you said what the pull-up value was, I didn’t see it. Can you say again what resistor you are using?

With my oscilloscope I can confirm the debounce is fantasic, going almost straight to ground in one microsecond with no bouncing. I should also mention I’ve confirmed the pins are tied up to 3.3v with the pullups and this reproduces on two identical breadboards that are wired the same.

Well, I missed that 3.3k resistor statement! Have you considered using a button debounce library?

I should also mention that I setup my logic analyzer to trigger on any excursion of the button 1 and 2 signals while pressing button 3. There are none. The problem seems to be with decoding the interrupt inside DeviceOS. This has nothing at all to do with debouncing.

I just moved buttons 2 and 3 from pins 1 and 3 to pins 17 and 16 and I can still get misbehavior. I sure hope this is my mistake but I ran out of ideas for how it could be, which is why I submitted this.

I uninstalled/resinstalled Visual Studio Code, Particle Workbench and the supporting tools. No change. I want to believe my environment is somehow polluted. A friend is bringing over the test program flashed into an argon to plug into my breadboards. I would agree with the proposition that if the OS runtime was so unstable to be calling the wrong handlers randomly nobody could get anything done. I’m just hoping to get a clue and report back in case whatever this is might be biting others.

It looks like this is the only button pin properly assigned. For an Argon device, your code assignment results in:

static const uint8_t USER_BUTTON_1_PIN = 9; // Pin Name: A4    ok
static const uint8_t USER_BUTTON_2_PIN = 1; // Pin Name: RST   ???
static const uint8_t USER_BUTTON_3_PIN = 3; // Pin Name: MODE  ???

Try using "Pin Name" for assignment of all your pins:

D2-D8 These are generic GPIO pins. D2-D8 are PWM-able. D7: Built-in LED.
A0-A5 These are analog input pins that can also act as standard digital GPIO. A0-A5 are PWM-able.

https://docs.particle.io/reference/hardware/pin-info/?m=table&sort=name

For example, try:

static const uint8_t USER_BUTTON_1_PIN = D3;  // using Pin Name      
static const uint8_t USER_BUTTON_2_PIN = D4;
static const uint8_t USER_BUTTON_3_PIN = D5;

static const uint8_t RED_LED_PIN = A3;
static const uint8_t YELLOW_LED_PIN = A4;
static const uint8_t GREEN_LED_PIN = A5;

As I mentioned, moving away from pins 1 and 3 had no effect on the problem. Also, D3 is in fact equal to 3 and D1 is equal to 1. You can see this with a print or inside the debugger and the defines are hiding out under .particle some place.

HOWEVER, I found the problem! The capacitors! Don’t ask me how, but with the capacitors removed (and software debounce used) the interrupts are reliable. I had connected an oscilloscope to the other pins, looking for them somehow being pulled down but the 'scope never triggered. It isn’t clear how it’s possible, but the pin 2 and 3 lines are somehow being glitched to a logic low. So my problem was self-inflicted. Here is the debounce circuit, with 100nF cap and 3K resistor that gave me this grief.
badbounce

Meant button 2 and 3.

Just to finish up this thread I thought I’d show the root cause of my problem. In the images below there is a schematic of the complete button hardware setup I was using, followed by two oscilloscope photos of the Particle interrupt line connected to “switch 2” glitching when “switch 1” was depressed enough times and the same glitch with a very fast horizontal rate. I’m not an EE and wouldn’t guess how this glitch on the line is taking place, but the sequential handling of the pin interrupts within DeviceOS in conjunction with this glitch that was completely invisible to my scope earlier (trigger voltage not set right) completely explains my confusion. Live and learn.