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

physicalMicrocontrollerLEDcircuitprototypeofresistor-300.JPG
DDRB.JPG

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

Microcontrollers - AVR Atmega32 - Understanding 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

Microcontrollers - AVR Atmega32 - Button Debouncing through Software

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;
         }
      }
   }
}