Debouncing button or switch inputs on microcontrollers can be a challenging problem for those first starting to program these devices. Part of the reason for this difficulty is that real-world buttons don’t behave like the idealized textbook components we first learn about, and therefore need special consideration to operate like one would expect. There are simple ways to debounce inputs like adding a delay after a button is pressed, but for more efficient use of computer resources as well as adding some other capabilities to inputs you might want to look at this interrupt service routine (ISR) method from [Lee] aka [stockvu].
The strategy with this debounce method is not simply to use a single ISR for the button input, but to activate a second timer-based ISR at that time that runs at a certain interval which timestamps any button press and checks the amount of time the button has been active. If it’s under a certain threshold the ISR assumes it’s caused by bounce and blocks the bounce. If the timestamp ages past another longer threshold it knows the button has been released. This method allows on-the-fly adaptation between long button presses and rapid button presses and is capable of debouncing both types.
For those wanting to try this out, [stockyu] has included some example Arduino code for others to use. It’s an interesting take on a solution for a common problem, and puts very little load on the microcontroller. There are about as many ways to debounce inputs as there are microcontroller platforms, though, and you can even use a 555 timer to get this job done which frees up 100% of the microcontroller’s CPU.
Yay, 555s Forever.
why bother. Slap a 0.1uf cap on the pin and be done with it. Some people just like overcomplicating things.
the issue is that this is not reliable enough in some cases. RC filter is all fine and well but charging the cap and discharging the cap happens at different rates. Once the cap slowly charges or slowly discharges there is a period of “insufficient signal to noise ratio” where a uC might not be able to reliably tell whether it’s a 1 or a 0.
I am not sure, if I understand correctly what “…not able to reliably tell wheter its a 1 or a 0” is meant to express but there is commonly a voltage hysteresis on I/Os for exactly that purpose: >~0.7xVdd=V_IH is “1” and <~0.3xVdd=V_IL is “0” so there is never a undefined “grayzone” as it just stays in the previous state between 0.7x and 0.3x.
Of course, if the capacitor is too small you do not get the effect and much too large will require longer button presses but with the 0.7 and 0.3 margin there should always be a value for sufficient SNR in my experience.
On the other hand, for a large scale design nobody would ever put a capacitor in there, if you can get the same for two lines of code for free.
I personally simply use a n++ for button-pressed and n– back to zero otherwise in the main-loop with some variable range guarding. This way, I do not care for bounces and can also define long and short button presses. Of course, the main-loop has to be somewhat fast enough otherwise this will fail or require quite long button pressing.
Between 0.3 and 0.7 x Vdd the digital input will still read a value, it is just undefined or unreliable. Some MCUs might default in that state to 0 or they might default to 1. The point is that operation in that voltage range is undefined and unreliable. It isn’t really a hysteresis either although it could be designed to be by just latching the value until it reaches the relevent threshold to indicate a flip but not all MCUs do that.
So inputting analogue voltages on a digital pin, which is what you are doing by using a button with an RC filter, can be unreliable although in that specific case it often works.
Schmitt trigger inputs have true hysteresis. Plenty of MCUs have those or they’re configurable as such. Downside is increased power consumption between the trigger points (delta-Ivcc) but that’s only rarely a concern.
An RC debounce adds latency to the input, though. For some designs it’s definitely noticeable: debounce times can be multiple milliseconds.
Just need to know your design targets. No one size fits all solution.
Are you maybe referring to older bipolar-based uC regarding this behavior?
Or could you just please name an uC that exhibits this “undefined” behavior? Just because in my understanding of CMOS designs with Vth of N- and PMOS having a solid seperation I would only see this kind of behavior at very high temperatures or with some kind of aging (e.g. PBTI, NBTI) or for very low supply voltages (sub-Vth operation).
I think, I understood your point now after thinking about the most simple I/O buffer implementation with just an inverter. That would have of course a steep transition at a certain small voltage range and adding noise on this voltage range would result in 1/0 transitions.
Luckily the I/O blocks have glitch filters besides schmitts etc. to prevent this for normal noise levels but I never checked how much uC manufacturers are “guaranteeing” in datasheets as it would be quite difficult to test this 100% in final test.
No, they definitely flip back and forth if the slew rate’s slow enough. You can see this pretty easily on lots of jelly bean IC if you just make super weak pulls. I regularly use I/Os as sleazeball comparators and you can use the fraction of 1s/0s to locate a transition more accurately.
It depends on the specific technology how they behave, but if you want clean transitions on a slow slew rate (us-scale) signal, you use Schmitt triggers.
Doesn’t matter what technology your MCU or IC uses it still has a defined threshold for 0 and 1, between those thresholds behaviour is undefined unless using something like a Schmitt trigger. If your MCU just uses the most basic forms of digital input then voltage inputs between the two thresholds will produce undefined behaviour which could affect your program in multiple ways, such as rapidly changing and unpredictable digital reads.
That’s a Schmitt input. It does have downsides: devices with Schmitt inputs draw a lot more current from Vcc when they’re in that region. So for really low power design it can be best to wake with an ISR, then disable the ISR and reenable with a later timer interrupt. Obviously costly with timers/interrupts and multiple inputs, but power’s power.
The Schmitt input will only see high current once in a blue moon when the button is changing state for a few milliseconds, so the added power use is basically of no consequence.
As for normal operation, just run a single ISR every 10-50 ms to poll all the inputs and keep a table of what inputs were seen. Then check the table for inputs in your main code. If you want to be clever, you can increment a number in the table to count elapsed time. That way if the input happens to be bouncing around just as the ISR fires to poll it, the worst consequence is that the state change in the table is delayed by one polling period.
It just depends on your use case. A constantly running ISR is not an option when your quiescent draw is required to be microamps.
So, you just increase the polling interval and sleep in the middle. Waking up for a couple microseconds every 100 milliseconds isn’t going to kill you.
Reading an input register and comparing whether anything has changed takes a handful of cycles, let’s say 100 cycles or about 6 microseconds for a 16 MHz MCU. If that happens every 100 ms then your are staying out of sleep for an additional 0.006% of the time. If your MCU is drawing 20 mA awake, the additional current draw would be around 1 µA which isn’t a problem but for the absolutely most power optimized cases. Typically you’re happy to go under 100 µA since that will last you 1-3 years on a battery, so one microamp difference isn’t a real difference. It’s well within the Iq leakage tolerances and variation of all your other components in the circuit.
So, instead of waking up from an input change, you simply wake up every 100 ms on the low frequency power saving timer to check the inputs. This has the added advantage that on a typical MCU only couple inputs are actually able to trigger a wake up from sleep, so you’d have to add additional components to OR all your inputs to the pin that actually calls the wake-up event. This way you don’t have to.
“Waking up for a couple microseconds every 100 milliseconds isn’t going to kill you.”
Again. It’s just use case. Polling requires power, it requires an active CPU, and more. I’ve had cases where the CPU must be flat off as much as possible. I’ve run a microcontroller off an aux I/O with sub milliamp current capacity where we had to track everything we could.
There’s no single solution that’s perfect. You just have to know what to use.
Yes, and I just put the power demand into perspective. Couple microseconds every 100 milliseconds does not break your power budget for anything but the most absolutely demanding applications where you’re aiming for a decade of battery life. The self-discharge rate of batteries and the quiescent current through other components will kill your application well before the cost of polling inputs every couple hundred milliseconds will do.
“Couple microseconds every 100 milliseconds does not break your power budget for anything but the most absolutely demanding applications”
Yes, that is literally what the phrase “know your use case” means.
It’s not just battery life, though. Energy harvesting will also do it (along with similar situations like isolated supply monitoring). Polling also doesn’t work if the MCU clock is shut down to a degree that it can’t wake up fast enough to poll (but the timer’s running because it’s quiet enough).
What are you talking about? Every RRC circuit must match the characteristic bouncing of that specific switch. that’s why, before you choose the RRC values you look the bounce and spikes vs. time in the oscilloscope. it’s not a solution just to put a capacitor. Additionally the application is very important in choosing any part of hardware, the switch, passive component values plus software second validation time, and read and forgive time.
It is if you choose the RC constant long enough.
Human response times are up to hundreds of milliseconds, which is a looo-ooo-ooo-ooong time for a switch to bounce. If you choose the RC constant around 10 ms, it would still take a switch specifically designed to bounce, or a broken corroded switch, to cause trouble. It’s not very critical, and for most cases you can just throw in “something”.
Be careful with that. Every time the charged cap is hard shorted by the contacts, significant currents flow for some nanoseconds. Not a big deal for microswitches with reasonably massive contacts, but definitely a thing to consider if you do not wanna your rotary encoder´s thin gold plating pitted.
That’s why there should be a series resistor, of the signal is coming from a sensitive contact or a semiconductor. In case of the microswitch, it may be advantages to forgo the resistor, as the brief high current will clean the contacts. Contact fouling is an issue for switches and relay contracts that only ever switch a negligible current.
Cc the manufacturer of the cursed digital scale in my kitchen that requires 5 attempts to turn it off every time
My kitchen scale is worse. “Tare”-ing it (zeroing it) requires 5 presses of the one touch-sensitive position under the glass front, but it will auto-shutoff within 30 seconds, which means that if you turn around to stir you lost how much you added.
Luxury. My kitchen scale requires several button presses to tare, AND the button at the front is mechanical, so if there’s not enough weight on it, pressing the button makes it skid around the counter. Naturally the load cells are in the feet, so you can’t hold the body in place with your other hand as that will throw off the reading. Obviously, it also has the mandatory 6ms auto shut off feature.
In case you’re wondering, this is the scale I bought after researching a less-frustrating replacement for the previous scale, which is now used for resin / silicone / plaster etc., and is ironically better designed.
Meanwhile, the analog scale my mom bought in 1982 has none of these problems.
“Progress” 🤦♂️
I haven’t looked at the code but a possible challenge here is that the first interrupt could fire at the rate of the bounce, either causing interrupt conflicts, overruns, or unnecessary CPU load if not handled correctly. Just something to consider when using interrupts for button debounce
Not a problem, bounce-caused Interrupts quick exit the ISR.
My approach would be to disable the pin change interrupt while in timeout, and enabling it again when the (single shot) timer ends. So only one of the interrupt sources is expected at a given time.
Yeah, that’s pretty standard. Sucks if you’ve got multiple inputs, although you can do interval sampling to be cheap (basically start periodically sampling at a key press, or just sample periodically, period).
If you need lots of buttons and super responsive inputs, probably best to handle it specifically in hardware anyway.
Another method is polling and iirc a “vertical counter” for the debounce logic. Does a whole port at once and requires very little memory as well as avoiding unscheduled interrupts.
Jack Ganssle did a two-part story on this with math, experiments and a lot of conclusions : https://www.ganssle.com/debouncing.htm
There’s an Arduino library that implements Jack’s conclusions well:
https://github.com/j-bellavance/EdgeDebounce
Make sure to check his other libraries too, Jacques Bellavance did several useful variations before he passed away.
With the clock from repurposed VCR parts I am in the process of making, I just scanned the status of keys every 20ms. This is long enough for the keys to not have any debouncing problems, yet short enough to feel instantaneous.
A small state machine makes it possible to differentiate between short, long and repeat key presses.
See https://iivq.net/scanning-keys/.
No interrupts whatsoever. This will work on most consumer electronics, but is too slow for things like keyboards or gaming rigs.
I use 100ms sampling time.
More than enough for a human-used button, we’re not that fast.
Works ok for most standard purposes, I use 20ms only for fast repeat push requirements.
Less code to write, less code to debug.
IRQs are more required with non-human interrupts IMHO.
Just sample 50 times/sec. If the button is pressed at that time, it’s pressed.
This. It’s like video game. You might drop video frames, but as long as you’re sampling at a steady rate the users expectation will be meet. User does a half moon on a game pad, you won’t miss it, even if the display runs at 5 frames per second.
Yep…irq to wake up only and immediately disable it. Don’t even bother reading it then. Just start Polling for input till you go back to sleep. If it’s good enough for gameboy it’s good enough period.
You don’t need interrupts to read switches. Just poll them with a ~10 ms period from the main thread
You’re assuming it’s okay to run every 10 ms.
If you’re running on wall power, it absolutely is. If you’re running on battery power, then the polling interval has to be chosen according to your intended charging interval.
If the intended application demands enough power that you’ll run out of battery in hours anyways, then having a standby demand in nano-amps isn’t really sensible. You’re better off just turning off entirely and having the user press a power button when they want to interact with the device.
No, it isn’t. It’s not just power, it’s also noise (and yes you can see a CPU clock running in sensitive analog), and it’s also what power is available. It doesn’t matter if you’ve got buckets o’ watts available if there’s no way to deliver it.
Sleeping and waking up can take a long time, so setting things to be interrupt driven can solve that: that ultra low power and quiet peripheral clock can stay running.
“then having a standby demand in nano-amps isn’t really sensible.”
They literally design stuff for this. I’ve used them. It’s totally sensible, just requires a lot of bookkeeping and planning. Finding sub-microamp leakages is actually kinda fun, you get to learn all the non-idealities of diodes, transistors, and caps.
Just by accident I found a very similar implementation from Microchip in Appliaction Note AN2805 (https://ww1.microchip.com/downloads/en/DeviceDoc/AN2805-Robust-Debounc-Core-Inddep-Periph-DS00002805A.pdf)
“Code Free Switch Debounce using TMR2 with HLT proposes a code-free solution, in which the very first switch activation is used to start the timer counting, ignoring any subsequent bouncing. Once the timer count reaches a predetermined value, the timer peripheral will produce a signaling event that can be used to indicate that a valid switch activation has been detected.”
I wrote a similar article on my blog : https://www.lambot.info/interruptions-multiples-sur-arduino/ (sorry it’s in french)
I also cover a workaround for the limitation of 2 interrupts on Arduino
If anyone is looking for a solution to this that is before the MCU I have been playing with the MAX6818.
It handles the timing and has a bunch of ESD protection, in octal form for data buses.
They also make it in a single lane version (MAX6816).
It is typically overkill for most stuff…but if you want a solution that doesn’t involve the MCU cycles or code…this is it.
https://www.analog.com/en/products/max6816.html
I can’t remember much about it, but in highschool (mid 90s) we had a switch that DID act like an ideal switch.
You pressed it, and a mechanism would go over center(?).
As it traveled, it made the connection somewhere in the middle and then kept moving to disconnect it. Then on the return trip it didn’t connect.
It didn’t matter how slow or fast you pressed it, or how hard. The actuator was not physically dependent on the button for anything but the release, and preloading the return spring.
Think of it like two opposed sear mechanisms.
Pulling the “trigger” compresses the return spring, then releases the first sear.
The switch connects, disconnects, then releases the second sear.
The return spring resets the switch.
Even if you hold the “trigger” down, it doesn’t reconnect to the mechanism until it travels back to the starting position, so it doesn’t jam.
It was probably expensive.
It probably required maintenance.
It was probably unsuitable for most uses.
But it was cool.
In many cases, it doesn’t matter if the interrupt blocks further execution for 50mSec or so. In those cases, you can simply add a short delay (10-50mSec) inside the ISR and read the switch or input again. if it is still in the same state, honor the input, if not, ignore it and exit the ISR.
Adding delay to ISR is really bad idea…
Most embedd D systems have a regular (usually 1khz) interrupt going.
Harness the beauty of Nyquist, and just sample the buttons at a regular rate. 1khz is usually slow enough, but you could always reduce it to something like 100hz.
Suddenly your buttons become “ideal” buttons.
I like more the idea, that you poll the state in some interval and save the state as bit in uint16/uint32. By shifting this value you get like floating window of values and then simply compare it to number of bits (stable time).
It’s pretty easy and fast method to use.