It is pretty much a given that any serious discussion of user interfaces and embedded design will eventually cover the subject of de-bouncing mechanical buttons. This simple intrusion of physics on the equally simple subject of user input has been a source of aggravation to more programmers than almost any other subject. Basically, all the screaming is about the mechanical bounce of switch contacts when the button is pressed. The problem is that the microcontroller is easily fast enough to see the multiple make-break transitions and report them as multiple key presses.
Most programmers handle this problem by sampling the input at a relatively slow rate, and then waiting for 3 consecutive low inputs before calling the button down. The release is generally taken to be the first high after a valid press. While this works and is relatively simple to implement, it does have an Achilles heal. If the contacts are closed, and the system is subjected to a shock, then bouncing contacts will be interpreted as a release followed by a new press.
One common method of fixing this problem is to just require 3 consecutive high inputs before calling the button released. The problem with this method is that a 3 sample system has to be fast enough to see at least one open during the bounce, while also slow enough to not see 3 closed samples during the bounce. Basically, 3 samples do not always allow enough of a window for the potential variability of the mechanical system. While we could expand out the system to 5 or 7, the overhead of keeping the last 5-7 samples become more complex, and more aggravating, plus the added de-bounce delay time will eventually become noticeable to the user. What we need is a system that starts the de-bounce process before the contacts stop bouncing.
The system that I use is based on the duty cycle of the bounce and it calls a button closed when the duty cycle exceeds 75%. I do this by using a single 3-6 bit counter, which I increment for every low sample, and decrement for every high sample. I also limit the counter so it can not roll over or under. When the count gets to 75%, I set the de-bounce output, and when it gets to 25%, I clear it. Using this system I can actually de-bounce the button before its contacts stop bouncing.
The C code for a single button is shown below;
IF (swx_input() == 0)
IF (count < (max+1)) count++;
IF (count > 0) count–;
IF (count >= (max*3/4)) button = 1;
IF (count <= (max/4) button = 0;