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
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.
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