Software Debouncing of buttons

Connecting a button as an input to a microcontroller is a relatively easy task, but there are some problems. The main problem is that switches bounce, i.e., when you press (or release) a button, it will often change level a couple of times before it settles at the new level. So if you, for example, connect the switch to a pin with an external interrupt enabled, you will get several interrupts when you press the button once. This behavior usually is not wanted. Even if the buttons didn’t bounce (with filtering hardware, for example), we still want to capture the event of a pushed button and take some action once for every button press, so we need to keep track of the state of the switch as well.

button_bounce

One technique used in this tutorial to handle this is to check (poll) the button(s) periodically and only decide that a button is pressed if it has been in the pressed state for a couple of subsequent polls.

When we connect a button to an input pin on an MCU we need to have a definite state both when the switch is open and when it’s closed. That can be accomplished with a resistor that pulls the port pin in one direction when the button is open. We can choose an active high or active low configuration.

resistor

Since AVR has an internal pull-up resistor, we usually choose the active low configuration shown on the left in the figure.  We can skip the external resistor and activate internal pull-up instead.

If we want to toggle a LED on or off every time a button is pressed, we can try this small program

bad_example

If we run this, we’ll see that the LED state after we press the button is more or less randomly because the LED is toggled (very) many times before the button is released. An easy but dangerous the solution to this is to add a delay for like one second after the LED toggle. But we usually don’t want to use delays (at least not that long delays) in real programs, and the button must be released within one second, and we can’t have more than one button press within one second.

We now want to check the button at regular intervals and consider it to be pressed if it’s in the same state for a couple of readings. Polling about every 10 ms and requiring four subsequent equal readings before the buttons state is changed usually works fine. Something like this in pseudo code

pseudo_code

We could write a function to do this and return a value that indicates if the button is considered to be pressed or not.

circuit_diagram

Here’s an implementation of the algorithm in the function debounce() together with defines and a main for a complete test program.

Example code

#include <avr/io.h>
#include <util/delay.h>
// Connect a button from ground to pin 0 on PORTA
#define BUTTON_MASK (1<<PA0)
#define BUTTON_PIN PINA
#define BUTTON_PORT PORTA
// Connect a LED from ground via a resistor to pin 0 on PORTB
#define LED_MASK (1<<PB0)
#define LED_PORT PORTB
#define LED_DDR DDRB
// Variable to tell main that the button is pressed (and debounced).
// Main will clear it after a detected button press.
volatile uint8_t button_down;
// Check button state and set the button_down variable if a debounced
// button down press is detected.
// Call this function about 100 times per second.
static inline void debounce(void)
{
// Counter for number of equal states
static uint8_t count = 0;
// Keeps track of current (debounced) state
static uint8_t button_state = 0;
// Check if button is high or low for the moment
uint8_t current_state = (~BUTTON_PIN & BUTTON_MASK) != 0;
if (current_state != button_state) {
// Button state is about to be changed, increase counter
count++;
if (count >= 4) {
// The button have not bounced for four checks, change state
button_state = current_state;
// If the button was pressed (not released), tell main so
if (current_state != 0) {
button_down = 1;
}
count = 0;
}
} else {
// Reset counter
count = 0;
}
}
int main(void)
{
// Enable internal pullup resistor on the input pin
BUTTON_PORT |= BUTTON_MASK;
// Set to output
LED_DDR |= LED_MASK;
while(1)
{
// Update button_state
debounce();
// Check if the button is pressed.
if (button_down)
{
// Clear flag
button_down = 0;
// Toggle the LED
PORTB ^= BUTTON_MASK;
}
// Delay for a while so we don’t check to button too often
_delay_ms(10);
}
}

3 Comments:

  1. Thanks for the code and explanation!

  2. It can be solved by adding a 0.1 UF capacitor across switches. 🙂

  3. The capacitor in parallel button will produce a small spark. Because of this, the surface of the contacts button gradually is destroyed and ceases to operate reliably.
    Software debounce method is more reliable.

Comments are closed