Search code examples
arduinointerruptavrisrdebouncing

Arduino Interrupt won't ignore falling edge


I'm having some trouble completely debouncing a button attached to to an interrupt. The goal is to have the statement in the void loop() run exactly once when the button is pressed/released.

What usually ends up happening is one of two things

  1. The ISR flag is set once when the button is pressed. Releasing the button does nothing, as intended.
  2. The ISR flag is set once when the button is pressed, and once more when the button is released.

Here is the exact code I have:

#define interruptPin 2

#define DBOUNCE 100

volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;

void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
  Serial.begin(115200);
}

void loop() {
  if(state){ //If we have a valid interrupt
    Serial.println(difference); //Print the time since the last ISR call
    state = LOW; //Reset the flag
  }
}

void ISR_0() {
  static unsigned long last_interrupt = 0;
  if(millis()-last_interrupt > DBOUNCE && digitalRead(interruptPin)){
    difference=millis()-last_interrupt;
    state = HIGH; 
  }
  last_interrupt = millis(); //note the last time the ISR was called
}

This seems to be a popular way to debounce an interrupt, but for whatever reason it isn't working for me.

I was hoping on the first falling edge of the button release that digitalRead(interruptPin) would read low, so the state flag would not be set.

Since the ISR updates the last_interrupt time, the successive bounces after the first falling edge still seem to be successfully ignored. This leads me to believe the debouncing is not the issue, but the digitalRead(interruptPin) is.

The debouncing seems to take care of all but one state. When the button is released, the code still occasionally sets the state flag to HIGH.

Here's some sample output:

3643 (after waiting ~3.6 seconds from boot, I press the button, releasing it ~1 second later)

In the same scenario as above, the output occasionally looks like this:

3643
1018

This shows me pressing the button, but also releasing the button.

I'm using an UNO R3 and a momentary tactile push button with a 1k pull-down resistor.

I'm not sure what's going wrong at this point. I hope this is simple enough that anyone can easily test this on their arduino if they feel so inclined.


Solution

  • Debouncing buttons/switches in interrupts is a hassle.

    I faced a (sort of) similar situation with limit switches. The moment a limit switch is hit, something has to occur - so interrupt.

    However the interrupt would fire when the limit switch was released as well which was a problem. I had my interrupt set to fire on a FALLING edge.

    Anyhow, I wound up debouncing outside of the interrupt, using flags. The code explains it but: Switch is hit, ISR runs (does what it needs to) and sets an ISR flag. The ISR flag stops the ISR from actually doing anything until it's cleared. In the main loop, call a debounce function if the ISR flag is set. the debounce function will wait until a pin/switch is stable at the required state (HIGH/LOW) for a predefined time, then clear ISR flag, allowing the ISR to do something again.

    #define interruptPin 2
    #define DEBOUNCE_TIME 100L
    
    volatile bool ISR_ACTIVATED = false;
    volatile bool display_int_time = false;
    bool debounce_started = false;
    unsigned long Isr_debounce_pin_timer;
    volatile unsigned long difference;
    
     void setup() {
    
      pinMode(interruptPin, INPUT_PULLUP);
      attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
      Serial.begin(112500);
    
    }
    
    
    void loop() {
    
      if (display_int_time) {  
        Serial.println(difference); //Print the time since the last ISR call
        // Right, done with interrupt stuff. clear the interrupt flag
        display_int_time = false;
      }
      // Call debounce ISR routine in main loop
      if (ISR_ACTIVATED)
        ISR_ACTIVATED = !debounce(interruptPin, HIGH, Isr_debounce_pin_timer);
    
    }
    
    bool debounce(int debounce_pin, bool state, unsigned long &state_latch_start) {
      // debounce_pin - what pin are we debouncing?
      // state - should the stable state be HIGH or LOW
      // state_latch_start - when we 'latched' on to a (possibly) stable state
    
      static bool current_state;
    
      // Read the required pin
      current_state = digitalRead(debounce_pin);
    
      // Is the pin at the required state, and we have not yet 'latched' it?
      if ((!debounce_started) && (current_state == state)) {
        // 'latch' this state (ie take note of when the pin went to the required level)
        state_latch_start = millis();
        debounce_started = true;
      }
    
      // Have we 'latched', but the pin has bounced
      if (debounce_started && (current_state != state))
        // unlatch
        debounce_started = false;
    
      // Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
      if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
        // cool. unlatch
        debounce_started = false;
        // report back that all is goood.
        return(true);
      }
    
      // Blast. Either the pin is at the wrong level, or is still bouncing. Tell the boss to try again later!
      return(false);
    }
    
    void ISR_0() {
    
    
      static unsigned long last_interrupt = 0;
      if((millis()-last_interrupt > DEBOUNCE_TIME) && digitalRead(interruptPin) && !ISR_ACTIVATED){
        difference=millis()-last_interrupt;
        //state = HIGH; 
        ISR_ACTIVATED = true;
        display_int_time = true;
      }
      last_interrupt = millis(); //note the last time the ISR was called
    }
    

    *** Just a note, edited my code to incorporate the debounce time in your original ISR to ensure that the interrupt is valid. I had not read the comment regarding your noisy environment