Debouncing buttons in AVR C++

On the face of it, reading the open/closed state of a button should be straightforward. You would just wire up the button circuit and tap the current flow into one of the Arduino digital pins configured for input.

Unfortunately you will soon discover that it’s just not that simple. At the point when a button’s contacts are being physically closed or opened the state of the electrical circuit is momentarily noisy. This means that your program will receive a rapid sequence of random HIGH/LOW signals which will certainly confuse it.

You could spend money and solve the problem in hardware with some circuitry around your buttons but it’s far easier to just write some code to do it. The technique is straightforward enough; when a button state change is detected we do not act upon it unless the state change lasts for longer than a preset short period of time, known as the debounce delay.

A C++ class solves the problem

I’ve written a reusable C++ class to do the debouncing for you. Here’s the header file.

#ifndef __99AC969B_B0C0_4ddb_BEDD_BF59AA339234
#define __99AC969B_B0C0_4ddb_BEDD_BF59AA339234

#include <stdint.h>

//
// Button implementation that does software debouncing
//

class DebouncedButton
{
private:
	// time to wait for bounce to clear
	static const uint32_t DEBOUNCE_DELAY_MILLIS=50;

  // Internal button state
  enum InternalState
  {
    Idle,													// nothing happening
    DebounceDelay,								// delaying...
  };

  // The digital pin where the button is connected
  uint8_t _digitalPin;

  // The pressed state (HIGH/LOW)
  uint8_t _pressedState;

  // Internal state of the class
  InternalState _internalState;

  // The last time we sampled our button
  uint32_t _lastTime;

public:

		// Possible button states
    enum ButtonState
    {
      NotPressed,						// button is up
      Pressed,							// button is down
    };

  // Setup the class
  void setup(uint8_t digitalPin_,uint8_t pressedState_);

  // Get the current state of the button
  ButtonState getState();
};

#endif

DebouncedButton.h

To use this class you must first declare an instance of DebouncedButton for each button in your project. For example:

DebouncedButton theButton;
theButton.setup(2,HIGH);

This would declare a button to be on digital pin 2 and that its pressed state reads HIGH. This feature allows you to handle normally open and normally closed buttons in this same class.

Next you simply poll the class whenever you want to know the current state of the button, for example:

if(theButton.getState()==DebouncedButton::Pressed)
{
  // do something
}

The getState() member function is asynchronous and will not block the caller at all, even for the duration of the debounce delay.

Source Code

Here’s the full source code to DebouncedButton.cpp so you can just copy and paste into your own project:

#include <wiring.h>
#include "DebouncedButton.h"


/*
 * Setup the class
 */

void DebouncedButton::setup(
  uint8_t digitalPin_,uint8_t pressedState_
)
{
  _digitalPin=digitalPin_;
  _pressedState=pressedState_;
  _internalState=Idle;

// set up the pin

  pinMode(digitalPin_,INPUT);

// activate the internal pull-up resistor

  digitalWrite(digitalPin_,HIGH);
}


/*
 * Get the current state
 */

DebouncedButton::ButtonState DebouncedButton::getState()
{
  uint32_t newTime;
  uint8_t state;

// read the pin and flip it if this switch reads high when open

  state=digitalRead(_digitalPin);
  if(_pressedState==LOW)
  	state^=HIGH;

// if state is low then wherever we were then 
// we are now back at not pressed

  if(state==LOW)
  {
    _internalState=Idle;
    return NotPressed;
  }

// sample the clock

  newTime=millis();

// act on the internal state machine

  switch(_internalState)
  {
    case Idle:
      _internalState=DebounceDelay;
      _lastTime=newTime;
      break;

    case DebounceDelay:
      if(newTime-_lastTime>=DEBOUNCE_DELAY_MILLIS)
      {
      // been high for at least the debounce time

        return Pressed;
      }
      break;
  }

// nothing happened at this time

  return NotPressed;
}

DebouncedButton.cpp

Test Project

For our test, let’s wire up the following simple circuit.

On the breadboard it looks like this. The red wire goes to +5V, blue to Arduino digital #2 and the 10K resistor is going to ground. This breadboard connects horizontal holes together as a strip.

Here’s some test code to demonstrate the button class. This example will flash the LED on the Arduino pin 13 when the button is pressed. The layout of this code is designed for Eclipse users. If you are using the Arduino IDE then you should just be able to copy and paste the setup() and loop() functions into the IDE Window.

#include <wiring.h>
#include <avr/wdt.h>
#include "DebouncedButton.h"


// Compatibility stub for undefined pure virtual

extern "C" void __cxa_pure_virtual() { for(;;); }


// the button class

DebouncedButton theButton;


/*
 * Main entry point
 */

int main(void)
{
	init();
	setup();

	for(;;)
		loop();
}


/*
 * Setup before loop
 */

void setup()
{
// setup the LED

	pinMode(13,OUTPUT);
	digitalWrite(13,LOW);

// setup the button on pin 2 (active LOW)

	theButton.setup(2,HIGH);
}


/*
 * Main loop
 */

void loop()
{
	uint8_t i;

// check if the button is pressed

	if(theButton.getState()==DebouncedButton::Pressed)
	{
	// flash it 3 times

		for(i=0;i<=6;i++)
		{
			digitalWrite(13,i & 1);
			delay(200);
		}
	}
}

main.cpp

Download the test project

The above test project is available for download, click here to go to the downloads page to get it.

The test project has a dependency on the Arduino libraries for Eclipse that you can also get from my downloads page. You may have to adjust the Eclipse compiler C++ include and link settings to reflect the location of the Arduino libraries on your PC.