You simply can’t trust switches. You think they’re either on or off, but the truth is that, in getting from one state to the other, they can change their mind many times.
This is a phenomenon known as switch bounce and it happens because switches aren’t perfect. Just at the point where they are making or breaking contact, certain mechanical or electrical effects can lead to that process happening multiple times.
After a while, the switch settles down into the state you’ve requested. And that ‘while’ can be very short indeed – microseconds, or even nanoseconds. But it can still be a problem.
From a human perspective, none of this matters. When you flip a light switch, you won’t even notice a slight flutter in the switch state (and there are electrical effects such as capacitance and inductance that may mask it anyway). But if the intended audience for the switch state is a computer or microcontroller capable of reacting very fast to that change of state, then it’s a whole different ballgame.
A program that responds to a change on, say, a GPIO pin needs certainty. Look at the following traces of a switch being switched.
And the same here, going from low to high. The computer is going to conclude that the state has changed from low to high, to low, to high. And this wasn’t the worst switch I’ve encountered.
Sometimes this doesn’t matter – it all depends on how the program needs to react to the change in state. But sometimes it matters very much.
The trend these days is to deal with this bouncing in software. You can find many tutorials about switch debouncing on the interwebs and one common method is to introduce a delay following a change of state – one that is long enough for the switch to settle. This isn’t necessarily the best method, as it blocks further operations until the delay is over, but it’s good enough for many situations. If the blocking is a problem, you may need to explore the world of interrupts.
And software debouncing has its benefits. For one thing, if you change the switch for one with different characteristics (such as the time it takes to settle down) you can accommodate this by a simple software/firmware upgrade.
But that doesn’t allow us to play around with electronics, so to hell with that.
Debouncing the hard way
At the heart of one approach to hardware debouncing is a simple form of the humble resistor-capacitor (RC) circuit. In the circuit above, let’s assume the switch is open. The capacitor will have charged. And the Vcc has a path to the GPIO pin via the two resistors, so the input on the pin will be high.
Now let’s push the switch closed. Now Vcc has a path to ground via just one resistor, R1. The left side of R2 also gets grounded so the potential at the GPIO pin is 0. However, it doesn’t just drop instantly from Vcc to 0. The capacitor discharges, and the rate at which it does so is controlled by the value of R2. Here’s what that transition looks like on the scope.
The capacitor, by maintaining some potential on the line for a while, has masked any bouncing of the switch.
Now let’s open the switch again. We return to the previous state. But, again, the presence of the capacitor, and the time it takes to charge, makes this a gradual affair.
One thing to note about the above two traces is that they are on very different timescales. The top one, with the switch closing, is 1ms per division while the bottom one is 100ms/div.
I decided to measure the time taken to change from 3.3V to 0V and vice versa (pretty arbitrary points, but let’s not quibble). With the switch opening it took 104ms. With the switch closing it was 4.3ms. This, of course, is all to do with time constants. I leave learning about capacitors and time constants as an exercise for the reader.
The precise characteristics of this debouncing are dependent on the values of the capacitor and the two resistors. In my test circuit, R1 is 10kΩ, R2 is 100Ω and the cap is 10µF. These were ‘experimental’ values (ie, picked at random). For much more detail on this circuit, and debouncing in general, check out Jack Ganssle’s in-depth guide.
You might think that would be enough, and in many practical applications you might be right. At some point on that curve, the microcontroller or computer that’s monitoring this line will decide that the signal is high or low enough to be considered on or off. The problem is that these two points are far apart. A chip might specify that ‘off’ is anything under, say, 0.8V while ‘on’ is anything above 2V. What about points in-between? Don’t go there. Seriously, it’s a death zone that you’re supposed to avoid at all costs. And yet the signal lines above spend quite a bit of time in that zone.
Here’s where the Schmitt trigger comes in. The Schmitt exhibits a property called ‘hysteresis’, which roughly translates as: ‘how I respond depends on what I was just doing’. So let’s put one in our circuit.
That inverter is actually a Schmitt trigger – in the case of my test circuit, it’s part of a 74HC14 chip that provides six Schmitt inverters.
The Schmitt trigger has two input thresholds for deciding the output state, but which one it uses depends on which way things are going.
Let’s start with a situation where the input to the Schmitt trigger is 0V and this has been stable for a while. And let’s assume, for the point of argument, that the Schmitt treats <=0.8V as low and >=2V as high.
Obviously, the current input is low, so that’s how the Schmitt treats it. (The Schmitt trigger in our schematic is an inverter, so its output will be high, or logic 1, but let’s not worry about that too much here.) But when in the low state, the Schmitt sets its threshold to the higher of the two – ie, it ain’t going to do anything unless the input gets to 2V (in our arbitrary example).
Now the voltage begins to climb. It gets above 0.8V which, with other components (including, possibly, our GPIO pin) would take us into the realm of ‘undefined behaviour’. But not with the Schmitt. Its attitude is: the input was low before and we haven’t yet reached the ‘high’ threshold (2V), so I’m going to continue treating this input as low.
When the voltage reaches 2V, the Schmitt flips, and accepts that the signal is now high. It also changes the threshold to the lower one. Here’s the circuit measuring the output of the Schmitt (remember, the output is inverted).
There’s a bit of overshoot and some ringing, possibly due to breadboard effects, but we can see that the transition is sharp (our timescale here is 50ns per division).
But what about when the voltage begins to fall? Remember that we’ve changed to using the lower threshold. As it drops just below 2V we get the mirror image of what happened earlier. Again we’re in no-man’s land. But the Schmitt says: I was high and I haven’t yet reached my defined low threshold, so I’m going to continue treating this as a high signal. Only when we get down to 0.8V does it change its mind and we’re back to where we started with the threshold again flipped to the higher one.
Here we’re on an even finer timescale – 10ns/dev. The time to go from 0V to 3.3V is just 2.6ns.
So what have we achieved? The RC circuit has eliminated the bouncing and the Schmitt trigger has given us a sharp, fast transition from on to off or vice versa, with no lingering in the death zone.
But what about that overshoot and ringing? Isn’t that a bit like bouncing? Well, it would probably be much reduced in a PCB circuit, as opposed to the breadboard lash-up I was using, but in any case…
You can see in the above image that on the low-to-high transition, all that stuff takes place well above 4V, so it’s all going to be treated as an ‘on’ state. And if we move the lower cursor to 0.8V, you can see that, again, the noise is safely below the threshold (and that the time to drop from 3.3V to 0.8V is just 1.6ns).
You don’t always need a Schmitt trigger. Some microcontrollers, single-board computers or ICs have a degree of hysteresis built into their pins. Check the datasheet for your favourite device to see what it says are the voltages associated with low and high levels (logical 0 and 1).
So, by this point there’ll be some reader screaming ‘Wrong! Wrong! Wrong!’. Debouncing is something of a religious issue. Some people will insist that you should always software debounce. Others will insist on different hardware debouncing methods. I don’t care. I embarked on this post because I wanted to use a Schmitt trigger and play with my oscilloscope (nothin’ wrong with that).
The truth is, I have always used software debouncing to date, and probably still will. But it’s good to have options.