Debounce Interrupt

I've been going through the examples and I decided to try to create a debounced button using an interrupt. This leaves the loop() function free to do complex tasks without having to monitor the state of the button.

Here is the code:

const int buttonInterrupt = 0;  // Button interrupt
const int buttonLedPin =  13;   // LED indicating the button state
const int switchLedPin = 12;    // LED indicating the switch state

volatile int buttonState = LOW; //initial button state

volatile boolean switchState = false; 

volatile long lastDebounceTime = 0;   // the last time the interrupt was triggered
long debounceDelay = 10;    // the debounce time; decrease if quick button presses are ignored

void setup() {
  pinMode(buttonLedPin, OUTPUT);
  pinMode(switchLedPin, OUTPUT);
  
  digitalWrite(buttonLedPin, LOW);
  
  attachInterrupt(buttonInterrupt, Rise, RISING);
}

void loop()
{
  
}

void Rise()
{
  long currentTime = millis();
  
  if ((currentTime - lastDebounceTime) > debounceDelay)
  {
    lastDebounceTime = currentTime;
    
    if (buttonState == LOW)
    {
      buttonState = HIGH;
      
      attachInterrupt(buttonInterrupt, Fall, FALLING);
    }
    
    digitalWrite(buttonLedPin, buttonState);
    
    Switch(buttonState);
  }
}

void Fall()
{
  long currentTime = millis();
  
  if ((currentTime - lastDebounceTime) > debounceDelay)
  {
    lastDebounceTime = currentTime;
    
    if (buttonState == HIGH)
    {
      buttonState = LOW;
      
      attachInterrupt(buttonInterrupt, Rise, RISING);
    }
    
    digitalWrite(buttonLedPin, buttonState);
    
    Switch(buttonState);
  }
}

void Switch(int state)
{
  if (state == HIGH)
  {
    switchState = !switchState;
  }
  
  if (switchState)
  {
    digitalWrite(switchLedPin, HIGH);
  }
  else
  {
    digitalWrite(switchLedPin, LOW);
  }
}

Since the millis() function does not update in an interrupt service routine and since calling the interrupt on CHANGE does not indicate the rising or falling state, the logic for debouncing a button is a little different here.

A button is attached to the interrupt 0 pin 2. A LED indicating the state of the button is attached to pin 13 and a LED indicating the state of an on-off condition (such as causing something happen or not happen in the loop() function) is attached to pin 12.

At the start of the sketch, the interrupt is attached to a routine called Rise that is fired when the button state goes from low to high. When this routing is called, we compare the value of millis() to the stored value of the last interrupt time. If the difference is less then debounceDelay, we change the state of the button and attach the interrupt to a service routine called Fall which is fired when the button goes from high to low.

Since the millis() function does not update during the interrupt service routine, the debounceDelay value can be very small since the noise created by the button will cause millis() to return values that are very close or exactly the same regardless of how long we spend in the service routine.

Here is how it looks:

Let me know what you think!

it's interesting, it seems complicated though to the point of being trouble-prone (certainly if i tried to write that or reproduce the idea I'd introduce some very hard to find bugs).

Switching the interrupt routine seems especially tricky - could you not use the mode where it will trigger on any change?

Do you actually need to do something asynchronously when the button is pushed?

If you fire the interrupt on CHAGE, you have no way to determine the state of the button to compare it to the last button state. I suppose you could call a digitalRead in the interrupt service routine, but it might not be reliable for very short button intervals.

If you fire the interrupt on CHANGE, you have no way to determine the state of the button to compare it to the last button state. I suppose you could call a digitalRead in the interrupt service routine, but it might not be reliable for very short button intervals.

Well, in theory that might be a problem, but anything that quick would be the very definition of a bounce wouldn't it?

Zoltan:
You could have a generic ISR which simply toggles the state of a status bit. The loop can then check the current state of the status bit.

volatile bool state = false;

//This interrupt service routine will simply toggle the state bit. Assume that the input is debounced or implement software debouncing in the below function
void genericISR() //triggered on CHANGE
{
      state = !(state) //sets state to true if it was false, or false if it was true
}

state_prev = state;

void loop()
{
      if(state_prev != state) //do something only on CHANGE of memory bit
      {
            if(state == true)
            {
                  state_prev = true;
                  //turn on LED
            }
            else
            {
                  state_prev = false;
                  //turn off LED
            }
      }
}

I would recommend a digitalRead() inside a while/for loop. The idea of debouncing is to watch the signal and swallow any further interrupts while it's bouncing.