Open Call: Send Us Your Debounce Code

If you’ve ever designed an embedded system with at least one button you’ve had to deal with button debouncing. This is also know as contact bounce, a phenomenon where a button press can be registered as multiple button presses if not handled correctly. One way to take care of this is with a hardware filter built from a resistor-capacitor setup, or by using a couple of NAND gates. We find that [Jack Ganssle] put together the most comprehensive and approachable look at contact bounce which you should read through if you want to learn more.

We’re interested in software solutions for debouncing buttons. This seems to be one of the most common forum questions but it can be hard to find answers in the form of reliable code examples. Do you have debounce code that you depend on in every application? Are you willing to share it with the world? We’d like to gather as many examples as possible and publish them in one-post-to-rule-them-all.

Send your debounce code to: debounce@hackaday.com

Here’s some guidelines to follow:

  • Please only include debounce code. Get rid of other unrelated functions/etc.
  • You should send C code. If you want to also send an assembly code version that’s fine, but it must be supplementary to the C code.
  • Please comment your code. This will help others understand and use it. You may be tempted to explain the code in your email but this info is best placed in the code comments
  • Cite your sources. If you adapted this code from someone else’s please include a note about that in the code comments.

As an example we’ve included one of our favorite sets of debounce code after the break. Please note how it follows the guidelines listed above.

/*--------------------------------------------------------------------------
10/13/2010: Button debounce code by Mike Szczys

based on "danni debounce" code by Peter Dannegger:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=189356#189356

This code detects and debounces button presses. It is tailored for use with
AVR microcontrollers but I've adapted it for other architectures easily and
successfully. It can be modified to use all eight bits on the same port
for up to eight buttons.

The interrupt service routine (ISR) at the bottom uses binary counter
variables (ct0 and ct1) to check the buttons once every 10ms until 40ms has
passed. If the button registeres the first and last times it reads it as
a keypress. There is no functionality in this code for detecting a held
button.
--------------------------------------------------------------------------*/

// F_CPU used by debounce to calculate 10ms interrupts
#define F_CPU 1200000

#include <avr/io.h>
#include <avr/interrupt.h>

//define pins used by buttons
#define KEY_DDR		DDRB
#define KEY_PORT	PORTB
#define KEY_PIN		PINB
#define KEY0		1	//Button on PB1
#define KEY1		2	//Button on PB2

//Debounce variables
unsigned char debounce_cnt = 0;
volatile unsigned char key_press;
unsigned char key_state;

/*--------------------------------------------------------------------------
  Prototypes
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask );
void init_timers(void);
void init_io(void);

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Used to read debounced button presses
  PARAMS: A keymask corresponding to the pin for the button you with to poll
  RETURNS: A keymask where any high bits represent a button press
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask )
{
  cli();			// read and clear atomic !
  key_mask &= key_press;	// read key(s)
  key_press ^= key_mask;	// clear key(s)
  sei();			// enable interrupts
  return key_mask;
}

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Sets and starts a system timer
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_timers(void)
{
  cli();			// read and clear atomic !
  //Timer0 for buttons
  TCCR0B |= 1<<CS02 | 1<<CS00;	//Divide by 1024
  TIMSK0 |= 1<<TOIE0;		//enable timer overflow interrupt
  sei();			// enable interrupts
}

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Initialize input and output registers
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_io(void)
{
  //Setup Buttons
  KEY_DDR &= ~((1<<KEY0) | (1<<KEY1));	//Set pins as input
  KEY_PORT |= (1<<KEY0) | (1<<KEY1);	//enable pull-up resistors
}

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Main
--------------------------------------------------------------------------*/
int main(void)
{
  init_timers();	//start the timer
  init_io();		//setup the buttons

  for (;;) //loop forever
  {
    if( get_key_press( 1<<KEY0 ))
    {
      //KEY0 press detected. Do something here
    }

    if (get_key_press( 1<<KEY1 ))
    {
      //KEY1 press detected. Do something here
    }
  }
}

//--------------------------------------------------------------------------
ISR(TIM0_OVF_vect)           // interrupt every 10ms
{
  static unsigned char ct0, ct1;
  unsigned char i;

  //TCNT0 is where TIMER0 starts counting. This calculates a value based on
  //the system clock speed that will cause the timer to reach an overflow
  //after exactly 10ms
  TCNT0 = (unsigned char)(signed short)-(((F_CPU / 1024) * .01) + 0.5);   // preload for 10ms interrupts

  i = key_state ^ ~KEY_PIN;    // key changed ?
  ct0 = ~( ct0 & i );          // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);       // reset or count ct1
  i &= ct0 & ct1;              // count until roll over ?
  key_state ^= i;              // then toggle debounced state
  key_press |= key_state & i;  // 0->1: key press detect
}

[Photo credit: Jack Ganssle]

75 thoughts on “Open Call: Send Us Your Debounce Code

  1. @macegr:
    I disagree with you there saying my method is not guaranteed to work…and the post by Brooks has done the work for me explaining why it does work in his last paragraph.

  2. @Dave McDave:

    I think Jack Ganssle makes the point that the bounce time can often be in the same order as your desired response time. So in other words waiting 100ms worst case to register a button push is annoying for the user. (I’ve just been trying this out on an FPGA board and actually anything over 25ms is pretty annoying with a tact switch.)

    To everyone who says a hardware solution is best, well it takes four passives and a schmitt input to do it properly. If you’re driving a micro anyway why not save some cash and board space and do it in software? I think the vertical adder posts prove it can be done in as little as 13 instruction cycles and two bytes of RAM per eight switches and no need for any external components.

  3. You need to know what frequency the switch needs to be closed at, and then ignore anything faster than this.

    I used to wind guitar pickups and wrote some software to act as a counter for a a coil winder. The switch was a reed switch that bounced horribly.

    Since the coil winder was variable speed, the debounce was written to sample the rate of the machine, and dynamically adjust the “ignore timing.” (The faster the winder went… the shorter the duration.)

    The only problem with this… the sampling of the rate was not instantaneous, and thus the acceleration had to be controlled. For pickup winding it did not tend to be a problem, but for other applications it might be.

  4. I always just run my push-buttons off of an output line, and bring the output line high only when testing (which, yes, is on a timer). I waste an output line, but I keep configurable repeat rate control, get the ability to turn on ‘hold’ in software, and don’t have to bother with making sure a separate voltage source is appropriate for an input line.

  5. Another clever idea that some people here may appreciate… Save on I/O by using an A/D input and a resistor ladder to read several button inputs, as opposed to having dedicated input pins. The debouncing gets relatively harder to do, but if your ranges are divided up evenly and you have enough resolution, it’s not much harder to decipher an A/D range over a binary.

  6. Why not just use non crappy switches and trigger on release where you have no bounce…

    button press required for 5ms, trigger on the release after that.

    holy cow guys 3 lines of code max. what N00b triggers on press?

  7. Mungewell, I think most microcontrollers already do buffer their I/O. There is a section in the AVR datasheets on metastability mitigation. I think is was hinted at that some PIC external interrupts (the level sensitive ones as opposed to edge sensitive) aren’t metastable safe, but the regular I/O ports are.

    fartface, “release here to start”, lol ;D

  8. I think it is a non issue.

    If you sample the keys on a 20ms interval there is no need to do anything else. If you have a keyboard you scan all the rows and columns each 20 ms. Obviously you use a timer interrupt for this.

    Sometimes you may sample a button while it’s bouncing but that doesn’t matter as it will be either in it’s last state or in it’s new state. And next time you sample it will be in the new state. So no double keypresses or anything like that.

  9. I dont understand all of this Rube Goldberg complexity with suggestions like counters for each button. :P

    If you need to debounce a button(s), just sample their inputs once every 25ms. The debounced value is (current_value | previous_value). It only requires one bit of RAM per button and eliminates all bounce with 0ms of on-delay and 25ms of off-delay..

  10. imho, it’s not worth debouncing a switch in code. Add in the 2-5 cents in components and get it over with is what I say.

    I already have trouble fitting my code into flash. I don’t need another headache.

  11. From the Ganssle article, now that it’s been cited twice (once in the original HaD post, once by Eardrill): “A medley of anecdotal tales passed around the newsgroups substitute for empirical evidence.” Heh. None of that happening here…

  12. fartface wrote:
    “Why not just use non crappy switches and trigger on release where you have no bounce…”

    Because this is really annoying, because many users hold buttons down long even if they only want a short action. Indeed, I am one of those. This depends on people’s traits, but some find it really annoying if something only acts when a button is /released/.

    Apart from that:
    “why not simply sample every 20ms and be done with it”:

    Because, dependent on the board layout and if wires are used to connect the buttons,
    EMI interference can soon be a serious issue.

    Try flicking an electric lighter (piezo-igniter) in front of your gadget.

    I mean that! Hold an electric lighter in front of your mikrocontroller bord, click it a few dozen times and see what happens.

    Surprised?
    Not?

    Try again!

  13. @fartface

    Trigger on release when you have no bounce? Because the first bounce from it being pressed will behave like a release… and then so will the next bounce… and then so will the release. If it bounces a couple of times, you will have multiple “releases” all the same.

    Though this problem is pretty simple, it isn’t NOT a problem entirely.
    -Taylor

  14. @signal7

    For many projects the additional parts cost next to nothing, you are right, but the additional boardspace may be priceless. Unless you have PLD or some other single chip solution adding 3-5 components per switch can add a lot of realestate even with SMD footprints. The resulting PCB manufacture then is no longer a trivial cost for the hobbyist, and may just outright ruin the look of the project too.

  15. What about multiplexed matrix keyboards? I don’t think an RC filter can be used to implement hardware debouncing for a 4×4 matrix keyboard, for instance. However, a software debounce algorithm can be applied to the sampled values over several sweeps of the keyboard matrix to produce debounced samples.

    While I agree hardware debouncing is great for many situations since it does slightly simplify the software, adding 3-5 components per input and most projects with user input have many more than 1 button, the added cost is probably not in the components themselves, but more in the layout complexity, number of vias, increased PCB area needed (possibly yielding fewer boards per panel), and assembly costs.

  16. Most of the time there is no need to overcomplicate things with complex filtering. With most push switches you can just sample them at regular intervals (say 50Hz like a games console samples the player’s fire button) and any bouncing will be filtered anyway. Unless the player can push the button at >25Hz that is, and IIRC the world record is 16Hz :)

    I recently made a clock with buttons. After a button is pressed I simply wait IIRC 500ms before sampling it again. That way you can push it once for a single increment or hold it for rapid increment (2Hz). All bouncing is filtered just by the delay.

    Even rotary encoders, which are notorious for being noisy, can be sampled at regular intervals without any problem. For the mouse connection on the Retro Adapter I just used a pin change interrupt to read the optical encoders.

    Bounce just isn’t a big deal in most systems.

  17. Many people don’t realise when you read the port pin, you’re only looking at the state for a very small time (~ns). Not only do we have contact bounce to contend with but also other forms of extraneous noise – ESD, EMI etc. For switches that are connected very close to the micro (fpga etc) EMI is probably not a big issue, however ESD is. Do you want your micro to interpret a ESD event as a button press? In any event, it makes good sense to sample your input a number of times to ensure it is in a known state whether or not the switch in question bounces. The more samples you take the more likely the actual state will be detected. There’s a tradeoff here. Also the idea of having switches connected to interrupts also has a potentially nasty side effect – say your input picks up some interference from a cell phone? You’re going to get a rash of interrupts in close succession that could cripple your micro as its busy servicing interrupts at a rate the programmer didn’t anticipate.By all means use an interrupt to bring the micro out of sleep if you want a button press to wake things up, but disable the interrupt once awake and use a timer interrupt to poll the switches.By design you want to make sure that the rate of interrupts is limited otherwise you might find your controller does weird things once in a while. If all you’re doing is flashing leds, the net result is minimal but if you’re doing something where the wrong decision might cause damage or injury, then you’d want to be more alert to potential problems. Your average industrial controller PLC will usually have a setting for the time it checks the inputs to be stable for and specific inputs where this doesn’t apply – eg counter inputs. Another gotcha is not to sample at or at a multiple of the mains frequency as if you synchronise, this can cause false inputs as well – the synchrosiation will drift in and out of phase causing an even weirder problem. So, guys and gals, contact bounce is a real problem, ESD and EMC is a real problem, so decide for yourself if you want to make a robust system or one that sorta ‘works’. Opinions vary – stick to the facts.

    For those who work with fpgas and logic, you will want to know and understand metastability – if you don’t it will bite you and it will hurt!

    Another one of my pet gotchas is atomicity. If you use interrupts, then you will want to understand this as well. A ‘one in a million’ problem can happen many times a second with a microprocessor.

  18. I would love to see some rotary encoder solutions. It’s a tricky issue because there is no “button” to hold down — so you can’t simply sample it every 30 milliseconds. Additionally, you need to allow it to be turned rapidly.

Leave a Reply to Pete SCancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.