Last time I wrote an introduction to buttons. Since the code to read buttons was so simple, thanks to the hardware abstraction layer (HAL), I spent more time explaining how buttons work electrically than you normally see on a software blog. That’s the nature of embedded systems: the dividing line between hardware and software is where we work.
At the end of last week’s post, I gave homework to get a button going and see if you get any weird results. You may have seen the LED flicker instead of turn on sharply and do it again turning off. Buttons don’t behave in a well defined manner, they go from open to closed, sure, but in the instant of opening or closing their mechanical nature comes into play, they bounce.
T-Talking About Noise G-G-generation
Push buttons are bistable they have two states (open and closed) where they reach a stable equilibrium. Unless they are disturbed, they will stay in those states. To achieve this, we use springs. The person pressing the button has to add force to the button to overcome the pressure of the spring to move it from one state to the other. But springs have this nasty habit of vibrating.
Since processors are very fast, we can sample the input pin that is being energized through a switch and sense the vibration of the conductive plate bouncing off of the contacts, opening and closing the circuit, rapidly, for a surprisingly long time. An ideal switch will go from open to closed, and closed to open instantaneously. But life just isn’t like that. Some switches will have metal fingers that slide across a contact until eventually landing on an insulator. And these switches will open and close and open and closes unpredictably until they become stable.
An oscilloscope is a very useful piece of equipment that, effectively, draw graphs with voltage on the vertical axis and time on the horizontal axis. Assume we have a switch connected to 3.3 volts, when we push our button, the voltage applied to our oscilloscope will be 3.3 volts. When we release our button, the voltage will drop to zero volts.
For example, in this picture that I grabbed from my oscilloscope (a Tektronix TWD120), we can see the action of a typical rocker switch that I ripped out of a PC power supply. The switch is simply connected between my bench power supply and my oscilloscope probe. On the left, the graph rises from around zero volts (marked with 0V on the vertical axis) and rises to 3.3 volts. The barely visible grid pattern in the picture is set for 0.5 volts per “division” or vertical spacing and 100 microseconds per division horizontally.
The trace rises at time zero (marked with a T on the horizontal axis) when the switch makes first contact, after 110 microseconds the voltage starts to drop as the switch opens up again when the contacts bounce then, after another 215 microseconds, the contacts mate again and the voltage becomes stable at 3.3 volts, then bounces again This particular switch takes over ½ of a millisecond to turn on properly.
Opening the switch gives similar results. The rocker switch has the contacts separate and reconnect two or three times before finally disconnecting.
When sampling the voltage on the processor pin, it’s not obvious when we can decide that the voltage is on or off. And what do we mean by on and off anyway? The voltages are not just 0 and 3.3, they are taking their time going from one state to the other.
Jack Ganssle wrote an article about debouncing buttons where he tests a variety of switches and gives circuit diagrams and code to combat the bounce. One of his switches took up to 157 milliseconds to settle.
What do you mean by “on” exactly?
The data sheet for the STM32F407 processor family shows voltages called VIL and VIH (voltage input low and high) which are the voltages at which an input signal to the processor is declared low and high.
The value of VIL is tested to be at most 0.3VDD. Our processor works with a VDD (supply voltage) of 3.3 volts. So our signals are declared low if it is less than about 1 volt.
The value of VIH is tested to be at least 0.7VDD, so any signal greater than 2.3 volts will be declared high.
In figure 1, the switch would apply 3.3 volts to the pin and be detected high, but the signal then drops below 1 volt and would be detected as being low. It then rises above 2.3 volts and is detected as being high again.
In figure 2, the signal has a blip but does not drop down to 1 volt until about 310 microseconds after the switch is disengaged. It is slow, but there shouldn’t be any glitches in the signal.
The low signal has to rise above 2.3 volts before it is declared high. And when high, drop below 1 volt to be declared low. When the circuit’s state depends on the past state as well as an input, the system is said to have hysteresis.
Up here, in The Great White North, we see hysteresis in action all of the time. If you cool down liquid water, the temperature will fall and then it will hang around the freezing point and not get any colder. Keep cooling and all of a sudden it turns to solid ice and the temperature goes down quickly. When heating up ice, again, it will warm and then hang around the freezing point, but doesn’t quite thaw. Then all of a sudden it becomes liquid. This is an example of hysteresis known as the latent heat of fusion.
What does that look like?
The IO ports on our processor are pretty cool. They have protection diodes to help the pins avoid going above the safe voltage or below ground. They have pull-up and pull-down resistors that you can turn on and off as you need.
The thing in the red circle in figure 3 is a Schmitt Trigger. A Schmitt trigger is the piece of hardware that implements the hysteresis in VIL and VIH. Our button’s sloppy signals are fed into the Schmitt trigger which takes a low input and generates a low output until the input rises above 2.3 volts at which time the output immediately goes to 3.3 volts. A high input to the Schmitt trigger makes a high output until the input drops below 1 volt at which time the output immediately snaps to zero.
The Schmitt trigger squares up our signal. It can get rid of some of the bounce, but not all of it. We need something to debounce our buttons even more.
Hold On, EE Ahead
Humans are pretty slow, and we need to slow down the buttons to get rid of the quick spikes. This is best done in hardware because then we don’t have to do any work. EEs say it should be done in software because you can reduce the board component costs by $0.006 per button by leaving off a capacitor and a resistor.
Who’s right? The software people, of course. A debouncing algorithm is not something that you can just call from your non-existent library of code, because embedded systems isn’t fair. The code has to be written and tested. This will take a few hours, and a few hours of my time will buy a hell of a lot of resistors and capacitors. But you only pay for software once (HAH), so if you are making a million boards go with software debounce and save the $6000. At a hundred thousand units you might break even.
For less than one hundred thousand units, use hardware. If you change button suppliers and the new buttons are noisy, just change the 0.3 cent resistor and capacitor and leave the code alone. Changing the code will cost about a day of time to get up to speed, adjust the timing, compile, test, tweak, test, review, check in, and update the code stuffing hardware.
Assume, for the moment, that electrons are water. A capacitor acts like a bucket to be filled with water from our hose-like button. Then the bucket has a hose out the bottom that flows into our processor. If the bucket is small, the ebbs and flows of our button will show up at the processor. If the bucket is huge, it takes a long time for the bucket to fill enough for any water to show up at the processor.
A resistor is somewhat like a water tap where you can adjust the water flow rate. If you put a resistor between our button and the capacitor, it slows down the rate at which the bucket will fill.
Let’s look at the schematic for our Discovery board; last week we had this picture:
The processor pin (PA0), when programmed as an input, has a very high resistance, about 3.3 Megaohms, so it will allow 1.1 microamps (3.3 volts / 3,300,000 ohms). Basically nothing. In any calculation dealing with this circuit, the processor can just be ignored; the processor is said to be high-impedance.
Let’s look at the various other components of the circuit and see what they do:
The solder bridge (SB20) is connected when you want to use the button (default) and removed when you want to use PA0 for something else and don’t want the button electronics interfering.
The R35 resistor is used to keep programmers from blowing up the processor. R35 restricts the amount of current that can go in or out of PA0 to (3.3 volts / 330 ohms = ) 10 milliamps. If R35 didn’t exist, and with PA0 programmed to be an output port set low, if you push the button, VDD will be connected directly to ground through PA0 for a fraction of a second, releasing the magic blue smoke.
R39 is the pull-down resistor. It is used to slowly drain off any charge that remains when the button is open. Assuming that the capacitor is charged up to 3.3 volts, this pull-down resistor will bleed down the voltage by 63%, to 1.22 volts, in 22 milliseconds (220K ohms * 100nF). This would still register as high, but at around 26 milliseconds it will finally switch off the Schmitt trigger.
R38 is the resistor used to increase the time it takes to charge the debounce capacitor when the button is open.
C38 is the debounce capacitor. It takes 12 microseconds to charge to 2.3 volts the when the button is open.
When the button gets pushed, 3.3 volts is immediately available to the bottom side of C38, making both sides of the capacitor 3.3 volts. EEs now say that there is zero volts difference on the two sides of the capacitor and it appears to be a conductor.
When the button bounces, capacitor C38 starts to discharge. The available paths for the charge to travel are through R35 to the processor, or through R39 to ground. Since the processor is high impedance, the amount of current that will flow through R35 is effectively zero. Going through R39 to ground, the signal would take 26 milliseconds to bleed down enough to be converted to a low by the Schmitt trigger. Luckily our button only tends to bounce for 0.5 milliseconds.
Therefore, the combination of R38 and C38 will get rid of button bounce for up to 26 milliseconds.
Okay, I lied. If you look at the real schematics for the board, you will find that C38 is labeled as “Not Fitted”, none of this stuff will actually work on the board as stuffed at the factory.
ST does not fit C38 because, as a development board, PA0 could be use for anything, like acting as a chip select on the SPI bus. With C38 in place, we could only put a 38Hz signal on the pin with any fidelity. Pretty useless really. (~0mS off to on + 26mS on to off = ~26mS = 38Hz)
However, if you are going to do is use PA0 to pick up the state of the button, and it’s going to be pushed by a puny human, 38Hz is plenty. Feel free to install a 100nF capacitor in the spot for C38 and you will get a nice clean button push level.
Okay, since our board doesn’t have C38, our button is going to be bouncy. How would you fix that in code?
The basic idea is that we periodically sample the button port and if the button has changed from low to high (pushed) that’s 1. Sample again a fraction of a second later, is it high still? Yes, that’s 2. Sample again, is it still high? No, set the counter to 0. Sample a bunch more times, incrementing when successful and clearing when unsuccessful, until your counter increments enough times to be considered stable. Do something similar for when the pin goes low.
Instead of writing some code, which will take a few hours to get right, I’ll just give you some links to some web pages with code:
- Jack Ganssle on debouncing - Jack has a two part post, surveying some of his stash of buttons, then explains how to debounce them in hardware, then gives code to debounce in software.
- Enrico Marinoni in Italia - Enrico gives an example of how to approach the debounce problem using the tick interrupt. The tick interrupt happens once per millisecond and in his interrupt routine he checks and debounces the button, leaving a clean button state for his normal processing.
- Patrick Hood-Daniel’s video and part 2- Patrick gives a good, hands on, example of software debounce, from explaining the problem, how he approaches the problem, and shows an example of code that goes directly to the device registers.
This post should give you a good grounding in buttons, their bouncey nature, and how to settle them down. The processor has signal shaping circuitry built in that will take care of some of the bounce and sags, but it is still necessary to take care of button bounce. Hardware is cheap, use it.
Next week, we’ll look at an extremely useful feature of computers called interrupts to get rid of that nasty polling.
This post is part of a series. Please see the other posts here.
This week’s music to work by: Sunday at the Village Vanguard by Bill Evans on Riverside records.