Beginning Microcontrollers Part 9: Button and Software Debouncing Methods
by RobotDictionary in Circuits > Microcontrollers
2905 Views, 14 Favorites, 0 Comments
Beginning Microcontrollers Part 9: Button and Software Debouncing Methods
In this part of the tutorial we will discuss button and software debouncing.
We want to study button debouncing first and in some detail so we have a good understanding of what it entails. Button debouncing is important and should not be underappreciated. Button switches are one of the ways that we create input to the microcontroller. When the button is pressed, we expect a reaction such as an LED blink, or a menu scroll. If a button has not been debounced in some capacity, we can become frustrated.
Button debouncing can cause multiple false button presses. Imagine using a button in the selection of a menu item. The button not being debounced, one click can cause the menu to skip one or more menu items. Even worse, when trying to select a particular item, and it continually skips when either button is toggled, making a particular selection to be made.
Button Debouncing
To illustrate button debouncing, the project we selected contains two LEDs. When the button is pressed, the LEDs toggle between one another. A button press turns one off and the other on. When releasing the button, it can start the process again and cause the LEDs to toggle again. You will notice the LEDs will toggle two times or more with only a single button press.
There are two ways to cripple debouncing. An in-circuit method (hardware) with use of a capacitor, and software debouncing. The hardware simply uses a capacitor to eliminate debouncing, and the software will create a variable that measures the confidence level of the button stream of ones or zeroes.
Disclaimer: This method that I use for hardware debouncing is a very simple and poor mans method. The main problem with this method is that the voltage climbs from 0 to 5v rather than an immediate, or instantaneous change. This can put the signal in a range that the microcontroller does not know how to deal with the voltage. This range is the area between the thresholds of high and low signals, which is between 2 and 3 volts. With this said, I have not personally seen any problem with this with my button debouncing. If you would like to eliminate this climbing, use a schmitt trigger.
In the video illustration you will see that the circuit is connected on the breadboard sans the hardware debouncing, so the problem can be experienced. Two LEDs are attached to the microcontroller, both on PORT B, one on pin 0 and the other on pin 2. Both of these pins will be set to output and since the LEDs are green, a 330ohm resistor is used for each LED. The button switch is connected to pin 1, on port B. This pin will be set for input and set to read high (pin set to a "1"). for the first "bounce" test, we will not use a capacitor across the two leads of the button.
The program to make two LEDs toggle when the push button is pressed is very simple. First, the pins are initialized: Pins outputting to the LEDs are set to output in the DDR (Data Direction Register). One of the LEDs are toggled high, so at the start, one is on and one is off. Then, the never ending loop is started and the code within that block gets executed until the microcontroller loses power. Withing this loop, the pin that is connected to the push button is constantly checked to determine if it is on. If it is pressed, and exhibits a 1, then it checks if the button was firsts released. This is important, because if we don't have this check, the button will just toggle continuously while the button is pressed. We only want the button to toggle if the button is pressed and then released.
#include <avr/io.h> int main(void) { DDRB |= 1 << PINB0; //Set Direction for output on PINB0 PORTB ^= 1 << PINB0; //Toggling only Pin 0 on port b DDRB |= 1 << PINB2; //Set Direction for Output on PINB2 DDRB &= ~(1 << PINB1); //Data Direction Register input PINB1 PORTB |= 1 << PINB1; //Set PINB1 to a high reading int Pressed = 0; //Initialize/Declare the Pressed variable
while (1) { if (bit_is_clear(PINB, 1)) //Check is the button is pressed { //Make sure that the button was released first if (Pressed == 0) { PORTB ^= 1 << PINB0; //Toggle LED in pin 0 PORTB ^= 1 << PINB2; //Toggle LED on pin 2 Pressed = 1; } } else { //This code executes when the button is not pressed. Pressed = 0; } <p> }</p><p>}</p>
When the microcontroller is programmed, and the button is pressed repeatedly, it becomes clear that the LEDs will toggle, sometimes correctly and sometimes multiple times with only one button press. Add the capacitor and check the button pressing and LED toggling again. On the oscilloscope, with the capacitor installed, a gradual rise of the voltage is created when the button is pressed, opposed to a bunch of up and down voltages resulting from a bounce from the mechanical parts of the button. But when the button is released, it shows that the voltage is a direct change. This is because another capacitor is not installed between the button and the microcontroller.
Software Debouncing
So why do we need to go over a new method when the other one seemed to work fine? Well, the software debounce method is essentially free if the program space and microcontroller cycle will allow it. With only a few lines of code, you can provide a lot more control of how the debouncing methods work with the button you're using. Debouncing in hardware may add additional costs to each developed board, and it is more difficult to determine a good debouncing for all the push button switches that will be used. However, if you want to preserve program execution cycles, it is best to go with the hardware route.
My method of debouncing with software uses only two variables that measure the confidence level of the actual button press. With the help of the Pressed variable introduced in the Button Debouncing, there will be a stream of 1's when the button is pressed, and a stream of 0's when the button is released. If there is bouncing going on, the stream of 1's or 0's will be very short, so we can take advantage of this by introducing two variables that measure the length of these streams. The variables are called, Pressed_Confidence_Level, to measure the button's pressed state, and Released_Confidence_Level to measure the button's released state.
So, if the button is pressed, the Pressed_Confidence_Level will rise, and the same with Released_Confidence_Level when the button is released. But, these variables will also become reset to 0 if the opposite condition exists. For instance, say the button was pressed for a time and the Pressed_Confidence_Level became a very large number, like 153,356. If the button was released (or a bouncing happened), the variable would be reset to 0. The trick to these variables is to determine a good threshold to determine a good button press or release state. Say, if the Pressed_Confidence_Level shows that after rising to 500, that this number s a strong indication of a button press, then the LEDs will toggle once. The same goes for the Released_Confidence_Level because bouncing could also happen on a button release. So, let's see how we do this in code:
int main(void) { DDRB |= 1 << PINB0; //For Notes on what these actions mean PORTB ^= 1 << PINB0; DDRB |= 1 << PINB2; DDRB &= ~(1 << PINB1); PORTB |= 1 << PINB1; int Pressed = 0; int Pressed_Confidence_Level = 0; //Measure button press confidence int Released_Confidence_Level = 0; //Measure button release confidence while (1) { if (bit_is_clear(PINB, 1)) { Pressed_Confidence_Level ++; //Increase Pressed Confidence Released_Confidence_Level = 0; //Reset released button confidence since there is a button press if (Pressed_Confidence_Level >500) //Indicator of good button press { if (Pressed == 0) { PORTB ^= 1 << PINB0; PORTB ^= 1 << PINB2; Pressed = 1; } //Zero it so a new pressed condition can be evaluated Pressed_Confidence_Level = 0; } } else { Released_Confidence_Level ++; //This works just like the pressed Pressed_Confidence_Level = 0; //Reset pressed button confidence since the button is released if (Released_Confidence_Level >500 { Pressed = 0; Released_Confidence_Level = 0; } } } }