Arduino: Software Debouncing in Interrupt Function...

69K2641

Intro: Arduino: Software Debouncing in Interrupt Function...

Hi everybody!

So recently i was working on a Project where i wanted to meassure the RPM of one of the Wheels on my Car, and from that calculate the Speed, keep track of the Driven distance etc. with an Arduino.
I wanted to use a Magnet and a Reed Switch to meassure the RPM, for that i needed to debouce the Signal from the Reed Switch, since i couldn't find any way to debouce a signal in interrupt functions that didn't require additional hardware for debouncing the signal on the Internet, here is how i debounced the Signal comming from the Reed Switch...




//Software debouncing in Interrupt, by Delphiño K.M.

long debouncing_time = 15; //Debouncing Time in Milliseconds
volatile unsigned long last_micros;

void setup() {
  attachInterrupt(0, debounceInterrupt, RISING);
}

void loop() {
}

void debounceInterrupt() {
  if((long)(micros() - last_micros) >= debouncing_time * 1000) {
    Interrupt();
    last_micros = micros();
  }
}

void Interrupt() {
  //Do Something
}

You could also "Do Something" in the main function, instead of calling a second function that then does something, i just liked it better this way...

Note: I've only tried this on an Arduino Mega (ATmega1280), with Arduino 0022.
Also, this isn't a perfect solution, the interrupt function is stil called multiple times, the follwing calls, in the next 15ms are just ignored, but hey, it does the job, so screw it!

Well, thats about it, i hope this helps someone...

P.S. This is my first Instructable, so if you don't have anything nice to say, don't say anything at all, allthough corrections, improvements, etc. are of course allways welcome...

36 Comments

This debounces my simple switch perfectly. Other solution are way to complex for such a simple thing.

Thank you very much!
I've seen software debouncing code that didn't use interrupts but used the main loop. It would only trigger some action if the state had changed, and then remained unchanged for a period of time. E.g. if 10 consecutive readings (one reading each millisecond) return the same (new) state, then do the action. I guess in some cases it could be good to do it this way, to prevent some spurious triggering (maybe due to some noise / glitch?), rather than do the action on first state change, then ignore further actions for a given time like your solution does. What do you think?
Do you think it would be feasible to implement this approach using interrupts? I'm guessing part of the code would have to go in the main loop, don't see a way around it.

I have a rain gauge that uses a tipping bucket with a reed switch/magnet to calculate fallen rain and have been having issues with random interrupt triggering. I tried this code and after updating the value to 30 milliseconds, I've been able to eliminate this problem. Thank you!

Hey, glad I could help. :-)
I suspect your bucket tipping not to happen quickly enough for you to necessarily need an interrupt for it though. Of course if you have an interrupt capable pin left over why not. But if you ever need to accomplish this without an interrupt, I'm sure reading the input in the main loop would do the trick (assuming you don't use delays and your main loop runs quickly).

void debounceInterrupt() {
if((long)(micros() - last_micros) >= debouncing_time * 1000) {
Interrupt();
last_micros = micros();
}
}

When micros() is overflow (after 70 minutes of idle stage), last_micros will larger than micros() at that time. The interrupt will not work until reach to last_micros again. To fix this issue, add below in loop().
if (micros()<last_micros) last_micros = micros();
Hey there thanks for the feedback, your concerns aren't an issue though. The code should work perfectly fine as is.
It is true that the "long" value of micros() will overflow after around 70 minutes due to the limited amount of numbers that can be expressed using 32 bits of memory. But this also applies when you subtract a very big number from a very small number with 32 bits of memory. 10-4294967290 would normally equal -4294967280 but a long type variable can't hold that value. Therefore long(10-4294967290) equals 16. I hope this explanation helps you understand overflows a little better.

I tried this code in combination with hardware debouncing like described in this article: http://www.ganssle.com/debouncing-pt2.htm When I keep the button pressed the interrupt is keep getting called. Why is this, when it is triggered on RISING edge?

Currently I switched to using an SR latch to toggle a state ON or OFF, but it requires 2 buttons. But I have not yet seen it fail and trigger multiple interrupts. And the interrupt routine now only requires 1 line: "state = !state"

On second thought, not happy with SR switch as the initial state of Q is unknown and can be either 1 or 0. Also I used 2 buttons and 2 interrupts.

I am now using a 555 in monostable mode (.5 sec output pulse width) which uses 1 button and is consistent when triggering a switch on RISING edge. I have not yet been able to trigger the interrupt multiple times.

GuusV, for the SR latch you don't necessarily need to buttons, but you would need to pins. You can use 1 button attached and use a pin attached to the latch's input to reverse the latch.

Why not simply do this to avoid needless interrupts during bouncing:

void debounceInterrupt() {
noInterrupts();
delayMicroseconds(15000);
interrupts();
// do something
}

Wow, this solution is so elegant.

Interrupts should execute as fast as possible though, so the microcontroller/computer can go back to the main loop. This especially plays a role when you have multiple hardware interrupts, since you can only have one triggered at a time. While your code would also work, it may 'cause problems in certain situations, 'cause the interrupt causes the program to pause for 15ms each time the interrupt is triggered. So if the interrupt is triggered 50 times per secons for example, your program would be waiting doing nothing 75% of the time.

I use the routine only for debouncing a tactile switch. It's no problem to pause the main program for 15 ms.

In that case you can do it that way, it's good practice though to keep your main loop (and especially interrupts) running as fast as possible. In some cases you will need every last spare moment your microcontroller has and in that case it's not a particularly good idea, to have it sit and wait though delays all the time. ;-)

Thank you, it seems to work well! But for my buttons I had to make the delay 300micros to get a good result.

Hi thanks for tutorial. I tested with Esp 12E and work great.

I signed up to Instructables just to say Thanks for this tutorial :)

Wow, thanks!

I'm glad if it helped you. :-)

(one time would have been enough though)

More Comments