Debounce Code – One Post To Rule Them All

Last month we asked you to send in your debounce code. You didn’t disappoint and it’s time to share the code received. There were some guideline for sending in code so if you don’t see yours here, it probably didn’t follow the rules, sorry. We also tried to weed out code that using delay loops for debounce. These tend to be a poor way to handle inputs because they monopolize the processor.

We wanted to add upvote/downvote buttons to each set of code to give some idea of a group consensus on code quality but there’s no good system available for multiple up/down vote widgets on one wordpress page. This results in a huge code dump for any one person to go through. If you’ve got any ideas on how to better organize this let us know: debounce@hackaday.com.

We make no guarantees that this code is safe to use, or that it even works. Test it carefully before using for important tasks.

Join us after the break for a whirlwind of code examples.

Ned’s Debounce Code

Ned sent in a package of debounce code that monitors multiple buttons, debounces them, and detects button hold and release.

Main.c:

/*****************************************************************************************
 * Project:			Button Code
 * Version:			V1.0
 * Client:			ProTechNZ
 * Date Created:	22/07/2010
 * Date Modified:	23/07/2010
 * Author:			Neil van Geffen
 * Company:			ProTech NZ

 * Micro:			ATMega128
 * Speed:			8MHz
 * Clock Source:	Internal
 *****************************************************************************************/

/************************* Defines *******************************************************/
#include <io.h>
#include <stdlib.h>
#include <string.h>
#include <delay.h>
#include "NedsStandardHeader.h"
#include "C Files\buttons.h"

#define BTN_UP			0x01
#define BTN_DOWN		0x02
#define BTN_ENTER		0x04
#define BTN_CLEAR		0x08

/************************* Structs and Enums *********************************************/

/************************* Function Prototypes *******************************************/
unsigned char TimeYet(unsigned int time);
unsigned int TimeDiff(unsigned int past, unsigned int future);
void USART0SendByte(unsigned char byteToSend);
void USART1SendByte(unsigned char byteToSend);
void USART0SendArray(unsigned char *array, unsigned char noOfBytes);
void USART0SendString(unsigned char *string);
void USART0SendStringF(flash unsigned char *string);

/************************* Global Variables***********************************************/
unsigned char tempByte;
unsigned int tempWord;

unsigned char pA, pB, pC, pD;

unsigned char releaseCounter;

volatile unsigned int isrTime;
unsigned int currentTime;
unsigned int timeButtons;
unsigned int clearPressed;

/************************* Setup Code ****************************************************/
void SetupPorts (void) {
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x04;    // RXD1 (2)
    PORTE = 0x01;    // RXD0 (0)
    PORTF = 0x00;

    DDRA = 0xF0;
    DDRB = 0xFF;
    DDRC = 0xFF;
    DDRD = 0xFF;    // TXD1 (3)
    DDRE = 0x02;    // TXD0 (1)
    DDRF = 0xFF;
}

// 1mS timer
void SetupTimers (void) {
	TCCR0 = (0 << FOC0) | (0 << WGM00) | (0 << COM01) | (0 << COM00) | (1 << WGM01) | (1 << CS02) | (0 << CS01) | (0 << CS00);	// CTC 8e6/64/125 = 1KHz
	OCR0 = 124;
	TIMSK |= (1 << OCIE0);
}

/************************* Main Code *****************************************************/
void main(void) {
	SetupPorts();
	SetupTimers();

	SREG.7 = 1;

	while (1)
	{
		SREG.7 = 0;											// Atomic Time Keeping
		currentTime = isrTime;
		SREG.7 = 1;

		pC &= 0x07;											// keep pC to a max of 7
		pD &= 0x07;											// keep pD to a max of 7
		PORTB = btnStatus.lastCount;						// output number of buttons pressed to LEDs on PortB
		PORTC = (0x80 >> pC);								// output a counter to PortC
		PORTD = (0x80 >> pD);								// output a counter to PortD

		if (TimeYet(timeButtons)) {							// Time to run this bit of code yet
			timeButtons += 5;								// Set the next time to 5mS away (can be any value really)

			UpdateButtons(currentTime);                     // Update the buttons
		}

		if (buttons.held) {									// If any button is held
			PORTF &= 0x0F;									// clear the high nibble off PortF
			PORTF |= (buttons.held << 4);					// and output the buttons held to PortF high nibble
			clearPressed = currentTime + 20;				// set the clearPressed time to 20ms (used to clear the LEDs after 20ms)

			switch (buttons.held) {							// do something depending on what buttons are held (can do a "case BTN_UP | BTN_DOWN:" if you wanted to as well)
				case BTN_UP:	pD++;		break;
				case BTN_DOWN:	pD--;		break;
				case BTN_ENTER:	pC++;		break;
				case BTN_CLEAR:	pC--;		break;
				default: pB++;
			}

			buttons.held = 0;								// Clear the buttons held flags
		}

		if (buttons.pressed) {								// if a button is pressed
			PORTF &= 0xF0;									// clear the low nibble
			PORTF |= buttons.pressed;						// and set the current puttons held to the low nibble
			clearPressed = currentTime + 200;				// set the clearPressed time to 200ms to get it to clear the LEDs after 200ms

			switch (buttons.pressed) {						// do something depending on what buttons are pressed
				case BTN_UP:	pD++;		break;
				case BTN_DOWN:	pD--;		break;
				case BTN_ENTER:	pC++;		break;
				case BTN_CLEAR:	pC--;		break;
				default: pB++;
			}

			buttons.pressed = 0;							// clear the buttons pressed flags
		}

		if (buttons.released) {								// if any buttons are released
			releaseCounter++;								// increment the release counter
			PORTF = 0x00;									// clear PortF LEDs
			PORTA &= 0x0F;									// clear the PortA high nibble
			PORTA |= (releaseCounter << 4);					// and set what buttons were released to tht PortA LEDs

			switch (buttons.released) {						// do something on a button release
				case BTN_UP:	pD = 0;		break;
				case BTN_DOWN:	pD = 7;		break;
				case BTN_ENTER:	pC = 0;		break;
				case BTN_CLEAR:	pC = 7;		break;
				default: pB++;
			}

			buttons.released = 0;							// clear the button released flags
		}

		if (TimeYet(clearPressed)) {						// if we should clear the LEDs
			clearPressed = currentTime;						// stop the time from wrapping-over
			PORTF = 0x00;									// clear the LEDs
		}
	}
}

/************************* Functions *****************************************************/
unsigned char TimeYet(unsigned int time) {
 	if (((time - 1) - currentTime) > 0xF000) {				// if the time has passed (will roll around when not serviced for 4095 counts)
    	return 1;											// the time has passed
    }
	else return 0;											// or else it has not yet passed
}

/************************* Interrupts ****************************************************/
interrupt [TIM0_COMP] void TimerZeroCompare (void) {
	isrTime++;												// Keep Time
}

Buttons.h:

/************************************* START OF LIBRARY COMMENTS *******************************
* Library Name:		Neds Button Code
* Version:			V1.0
* Created:      	22/07/10
* Last Mod:			23/07/10
* CV Version:		2.04.8a
* Author:       	Neil van Geffen
* Company:      	ProTechNZ
* Purpose:      	Read 4 buttons and return button presses, helds and releases.
************************************************************************************************/

/************************************* KNOWN BUGS **********************************************
*
************************************************************************************************/

/************************************* NOTES ***************************************************
* The code will decode the button presses into presses, holds and releases.
* A press is a press AND release before a hold is registered
* A hold is a press held long enough to register as a hold.
* A hold will automatically repeat itself at an increasing rate
************************************************************************************************/

#define BUTTONS			(PINA & 0x0F)		// Make the buttons the lower nibble active high (use ~ to get active low buttons to appear as active high)

#define REPEAT_MAX		250					// The start value of repeat debouncing when first pressing a button
#define REPEAT_MIN		25					// The lowest value of repeat debouncing when first holding a button
#define SPEED_SHIFT		3					// The repeat value decreases by the current repeat value >> by this value (aka 4 means it decreases by 1/16th every time)
#define DEBOUNCE_PRESS	25					// The debounce for a single press
#define DEBOUNCE_HOLD	600					// The debounce for a button hold

struct {
	unsigned int pressed:4;			// holds which buttons have been pressed and released
	unsigned int held:4;			// holds which buttons have been held for more than DEBOUNCE_HOLD
	unsigned int released:4;		// holds which buttons have been released after a button was held
} buttons;

#pragma used+
/***** UpdateButtons
 * Read 4 buttons (defined as BUTTONS above)
 * and save them to the buttons struct.
 * Best if called on a regulat basis like every 10mS.
 * Calling more often will give better resolution on button presses.
 ----------
 * @param - curretTime, the current time to compare the last press too to calculate debounce and press held length
 *****/
void UpdateButtons(unsigned int currentTime);
#pragma used-

#include "buttons.c"

Buttons.c:

/************************************* START OF LIBRARY COMMENTS *******************************
* Library Name:		Neds Button Code
* Version:			V1.0
* Created:      	22/07/10
* Last Mod:			23/07/10
* CV Version:		2.04.8a
* Author:       	Neil van Geffen
* Company:      	ProTechNZ
* Purpose:      	Read 4 buttons and return button presses, helds and releases.
************************************************************************************************/

/************************************* KNOWN BUGS **********************************************
*
************************************************************************************************/

/************************************* NOTES ***************************************************
*
************************************************************************************************/

#define BUTTON_COUNT	((BUTTONS && (BUTTONS & (1 << 0))) + (BUTTONS && (BUTTONS & (1 << 1))) + (BUTTONS && (BUTTONS & (1 << 2))) + (BUTTONS && (BUTTONS & (1 << 3))))

#warning By compiling Neds button code, you acknowledge he is the man!

struct {
	unsigned char heldFlag:1;		// used by neds code, never change
	unsigned char decreaseFlag:1;	// used by neds code, never change

	unsigned char lastStatus:4;		// used by neds code, never change. The last valid combination of buttons pressed
	unsigned char lastCount:4;		// used by neds code, never change. The number of buttons held at one time

	unsigned int time;				// used by neds code, never change. The time the button press was changed

	unsigned int repeat;			// used by neds code, never change. The time between button held repeats
} btnStatus;

unsigned int TimeDiff(unsigned int past, unsigned int future) {
	if (((future - 1) - past) > 0xF000) return 0;
	else return future - past;
}

void UpdateButtons(unsigned int currentTime) {
	if (TimeDiff(btnStatus.time, currentTime) >= DEBOUNCE_HOLD) {									// If a button has been held
		if (btnStatus.decreaseFlag) {																// if the button count was lowered earlier but they have remained the same for the length of a hold time
			btnStatus.decreaseFlag = FALSE;														// clear the flag that states it was lowered
			btnStatus.lastStatus = BUTTONS;														// and set the button status to the currently pressed buttons
		}

		buttons.held = btnStatus.lastStatus;																// Set what buttons were held
		btnStatus.time += btnStatus.repeat;																// and set the time to repeat the next press
		btnStatus.repeat = MAX(REPEAT_MIN, btnStatus.repeat - MAX(1,(btnStatus.repeat >> SPEED_SHIFT)));		// and lower the repeat value to increase the button held repeat rate
		btnStatus.heldFlag = TRUE;																			// and set the flag that states a button was held
	}

	if (!BUTTONS) {
		if (btnStatus.heldFlag) {																	// If the buttons were previously held
			btnStatus.heldFlag = FALSE;															// Clear the flag so it doesnt set buttons pressed continously
			buttons.released = btnStatus.lastStatus;												// Set what buttons were pressed previously
		}
		else if (TimeDiff(btnStatus.time, currentTime) >= DEBOUNCE_PRESS) {						// but if the buttons werent held, but pressed for long enough to pass as a debounce
			buttons.pressed = btnStatus.lastStatus;												// Set what buttons were pressed
		}

		btnStatus.lastCount = 0;																	// Clear the last count
		btnStatus.lastStatus = 0;																	// Clear the last Status
		btnStatus.time = currentTime;																// clear the last press time
	}
	else if (BUTTON_COUNT > btnStatus.lastCount) {												// if the number of buttons pressed has changed
		btnStatus.lastCount = BUTTON_COUNT;														// save it for next time.
		btnStatus.lastStatus = BUTTONS;															// and save what buttons were pressed.
		btnStatus.decreaseFlag = FALSE;															// clear the flag that says the button presses just decreased.

		btnStatus.time = currentTime;																// reset the time of last button presses.
		btnStatus.repeat = REPEAT_MAX;															// and reset the time between held repeats.

	}
	else if (BUTTON_COUNT && (BUTTON_COUNT < btnStatus.lastCount)) {								// Or if the button count deceased but a button is still pressed
		btnStatus.lastCount = BUTTON_COUNT;														// save the count for next time
		btnStatus.decreaseFlag = TRUE;															// set the flag to say this happened

		btnStatus.time = currentTime;																// reset the time of last button presses.
		btnStatus.repeat = REPEAT_MAX;															// and reset the time between held repeats.
	}
	else if (!btnStatus.decreaseFlag && (BUTTONS != btnStatus.lastStatus)) {						// If someone changed button presses but not the count
		btnStatus.lastCount = 0;																	// Force the count to change next time around so the code to set times etc isnt in 2 places.
	}                                                                   						// This is a fairly useless bit of code if the service time is less than 10mS and even if its more, it won't be all that usefull.
}

S1axter’s Debounce Code

Developed for PIC24 chips, this code repeatedly calls a function to check a pin state.

pin_io.h:

//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Pin I/O control module - Header
//
// Language: Microchip C30
//
// File:     pin_io.h
// Author:   MyBitBox.com/Geeksinside.com
// Created:  08/23/09
//
//////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __PIN_IO_H__
#define __PIN_IO_H__

void pin_io_init(void);
void pin_input_check(void);

#endif

pin_io.c:

//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Pin I/O control module
//
// Language: Microchip C30
//
// File:     pin_io.c
// Author:   MyBitBox.com/Geeksinside.com
// Created:  08/23/09
//
//////////////////////////////////////////////////////////////////////////////////////////////////////

typedef struct{
	uint16 mask;
	uint16 last_state;
	uint8 changed;
}pin_check_struct;

pin_check_struct inputA, inputB;

//====================================================================================
// Set up the pins
//====================================================================================
void pin_io_init(void)
{

	inputA.changed = FALSE;
	inputA.last_state = FALSE;
	inputA.mask = BMASK(2);       // look at PORTB2

	inputB.changed = FALSE;
	inputB.last_state = FALSE;
	inputB.mask = BMASK(4);        // look at PORTB4

	return;
}

//====================================================================================
// This is the debounce routine. When this is called it checks for consistant pin states
//====================================================================================
void pin_input_check(void)
{

	uint16 portb_snapshot = PORTB;

	// This is for the XXXXX input
	// ------------------------------------------------------
	if(inputA.changed == TRUE)
	{

		if(!((portb_snapshot ^ inputA.last_state)&inputA.mask))
		{
			// If the line was changed last time, and it is the same state as last
			// time, then we need to lock it in here (If the bits are not the same then this routine
			// will be called again and the correct value will be locked in)
			if(portb_snapshot & inputA.mask)
			{
				// Do this when the line goes high
				SYS_STATUS.FLAG_XXXXX_LINE = TRUE;
			}else{
				// Do this when the line goes low
				SYS_STATUS.FLAG_XXXXX_LINE = FALSE;
			}
			// Clear the changed flag
			inputA.changed = FALSE;
		}
	}
	// Mask out any changed input pins
	inputA.changed = ((inputA.last_state ^ (portb_snapshot & inputA.mask))>0);		// XOR with last last_state to find changed pins
	inputA.last_state = portb_snapshot & inputA.mask;

	// This is for the YYYYY input
	// ------------------------------------------------------
	if(inputB.changed == TRUE)
	{

		if(!((portb_snapshot ^ inputB.last_state)&inputB.mask))
		{
			// If the line was changed last time, and it is the same state as last
			// time, then we need to lock it in here (If the bits are not the same then this routine
			// will be called again and the correct value will be locked in)
			if(portb_snapshot & inputB.mask)
			{
				// Do this when the line goes high
				SYS_STATUS.FLAG_YYYYY_LINE = TRUE;
			}else{
				// Do this when the line goes low
				SYS_STATUS.FLAG_YYYYY_LINE = FALSE;
			}
			// Clear the changed flag
			inputB.changed = FALSE;
		}
	}
	// Mask out any changed input pins
	inputB.changed = ((inputB.last_state ^ (portb_snapshot & inputB.mask))>0);		// XOR with last last_state to find changed pins
	inputB.last_state = portb_snapshot & inputB.mask;

	return;
}

//end

Aaron Keith’s Debounce Code

This code is tailored for an 8051 processor.

Button_debounce_8051.c:

/*--------------------------------------------------------------------------
10/14/2010: Button debounce code by Aaron Keith

This code detects and debounces button presses. It is tailored for use with
8051 micro controllers.  Complied on the RIDE 51 Complier

The interrupt service routine (ISR) runs 3600 times per second.  If the
button is pressed (the pin is connected to GND) Key_Hit is incremented to the
maximum 255.  When the button is released Key_Hit will decremented to 0.
Long_Key_Hit will increment more slowly and is used to detect the button
being help down.
--------------------------------------------------------------------------*/

#include<reg51.h>

#define TRUE 1
#define FALSE 0

//define pins used by buttons
at 0x97 sbit P1_7 ;
#define BUTTON P1_7   //Button on Port 1.7

//De bounce variables
unsigned char Key_Hit;
unsigned char Long_Key_Hit;

/*--------------------------------------------------------------------------
  Prototypes
--------------------------------------------------------------------------*/
void init_timers(void);

/*--------------------------------------------------------------------------
  FUNC: - Sets and starts a system timer
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_timers(void)          // using a 11.0592 Mhz Clock
{
  TMOD = 0x02; //T0 mode 2 8 bit reload
  // Timer 0 is system tick

  TH0 = 0x00; // Reload = 256, giving 921600/256=3600
  TL0 = 0x00;

  ET0 = TRUE; // Enable interrupt on timer 0
  TR0 = TRUE; // Start timer 0;

  EA = TRUE;
}

/*--------------------------------------------------------------------------
  FUNC:  - Main
--------------------------------------------------------------------------*/
void main(void)
{

  init_timers(); //start the timer

  for (;;) //loop forever
    {

      if (Key_Hit == 0xff)
        {
          //Key press detected. Do something here
        }

      if (Key_Hit == 0x00)
        {
          //Key release detected. Do something here
        }

      if (Long_Key_Hit == 0xff)
        {
          //Key press and help down. Do something here
        }

    }

}

/*--------------------------------------------------------------------------
  INTERRUPT:  // The procedure runs 3600 times per second
--------------------------------------------------------------------------*/
void Timer0_interrupt(void) interrupt 1
{

  static unsigned char Div18Counter = 0;

  if (BUTTON)
    { // Button up (Pin is connected to VCC)
      if (Key_Hit != 0)  // Will never go below zero
        {
        Key_Hit--;
        }
    }
  else
    { // Button down (Pin is connected to GND)
      if (Key_Hit != 0xff)  // Will never go above 255
        {
        Key_Hit++;
        }
    }

  if (++Div18Counter >= 18)   // run ever 5 mSec
    {
      Div18Counter = 0;

      if (Key_Hit == 0xff)
        {
          if (Long_Key_Hit != 0xff)
            Long_Key_Hit++;
        }
      else if (Key_Hit == 0x00)    // No delay when detecting key release.
        Long_Key_Hit = 0x00;
    }

}

Thomas Flayols’ Debounce Code

This code creates a software-based low-pass filter by taking a moving average of the pin value.

// Hi, to prevent contact bounce, my solution is to create software low pass filter by using a moving average...(no delay solution) see the jpg to see what's happen
// INPUT is the button input pin(0if not pressed, 1 if pressed)

main(void)
{
    unsigned char button = 0;
    while(1)
    {
         // your code here...

         button=(button * 9+INPUT * 10)/10; // do a moving average of the digital input... result button between 0 and 10

         if (button > 5)
         {
                //the button is ON
         }
         else
         {
                //the button is OFF
         }

    }
}

Ganesh Krishna’s Debounce Code

Can be scaled to any number of buttons, sample rate is configurable, and this aims to use as little RAM as possible

dbounce.c:

#include <htc.h>

/* Scalable software debounce for buttons
 *
 * This is quick implementation of software debounce with as little RAM as possible.
 * stress is given to scalability of number of buttons
 * and scalability of number of samples per debounce cycle.
 *
 * While this file is written for PIC Microcontroller and Hi-tech compiler,
 * the debounce function itself is just C, and not hardware dependant.
 *
 * Use the functions at your own risk
 * Author: ganesh.ksm@gmail.com
 *
 * Assume BSD like license.
 * Acknowledge with an email if it works for you.
 *
*/

/*Number of buttons in the system*/
#define MAX_BUTTONS 4

/* MAJORITY_VOTE Number of samples for which the button must remain in pressed state
 * to consider that the button is pressed.
 * */
#define MAJORITY_VOTE 5

/* STABILITY_BITS is the number of final samples (last few samples at the end of debounce)
 * after which we consider the input stable
 * */
#define STABILITY_BITS 2

/* Convert Stability bits to a bit mask, i.e STABILITY_MASK has STABILITY_BITS bits set in its LSB
 * */
#define STABILITY_MASK ((1<<STABILITY_BITS) - 1)

/*This variable contains the debounced status of all buttons
   at any point of time
   */
volatile unsigned int debounced_buttons;

/* Reads port pins and returns 1 if button is active (assumed active low)
 * returns 0 if inactive. Microchip PIC18 specific.
 * */
unsigned char Read_Portbit(int button)
{
	switch(button)
	{
		case 0: return !RA0;
		case 1: return !RA1;
		case 2: return !RA2;
		case 3: return !RA3;
	}
}

/* Function: button_is_pressed()
 * Argument: Takes the button number as argument, an index into the array of buttons.
 * Returns:  non zero if the button of interest is pressed for majority time of polling
 *           returns zero if not pressed, debouncing not complete,
 *           button bouced back to unpressed state
 *
 * Calling rate: This function will return positive only after MAJORITY_VOTE is achieved
 *  times with same button argument for debounce of one button.
 * 		 where X is the number of samples per debounce,
 *
 * Depends on: Read_Portbit(button) which returns 1 if the pin of button of interest is active and 0 if not.
 */
char button_is_pressed( unsigned char button)
{
	unsigned int bit_count=0, temp;

	/* button_bits[] is an array to hold the previous states of the port pin value
	 * Each element holds the previous samples of teh button read.
	 * We need as many elements as there are buttons.
	 * Make this an integer array if more than 8 samples are needed for debounce determination.
	 * if less samples are needed adjust the MAJORITY_VOTE and STABILITY_BITS to a smaller number
	 * */

volatile static unsigned char button_bits[MAX_BUTTONS];

	/* Shift the latest read button status into the que
	 * we should have the latest sample in LSB and older samples
	 * higher up.*/

	button_bits[button]=button_bits[button]<<1 | (Read_Portbit(button) & 0x01);
	temp = button_bits[button];

	/*Check if the input is stable in the last STABILITY_BITS samples*/
	if ((temp & STABILITY_MASK) == STABILITY_MASK)
	{
		/* Count the number of bits set in temp, we need more than the majority
		 * straight out of the book of K&R, check it :-)*/
		while(temp = temp&(temp-1))
			bit_count++;
		bit_count++; // we are off by one

		/*Check if the required number of samples were favourable */
		if (bit_count>=MAJORITY_VOTE)
		{
			button_bits[button] = 0;
			return 1;
		}
		else
			return 0;
	}
	else
	{
		return 0;
	}
}

/* Call at a rate about 8 times higher than the rate at which input detection is required
 * actual frequency depends on the number of samples required per debounce.
  */

void Service_user_Input(void)
{
	int i;
	for (i=0;i<MAX_BUTTONS;i++)
	{
		if (button_is_pressed(i))
			debounced_buttons = debounced_buttons | 0x01<<i;
	}
}

void main (void)
{
	/*PIC18 specific intialization of timer interrupt*/
	GIE=1;
	PEIE=1;
	TMR0IE=1;

	T0CON= 0X48;    //16 bit timer without Prescaler for interrupts
	TMR0L=0xFA;
	TMR0H=0xFF;
	TMR0ON=1;
	TRISA = 0xFF; // PORTA is input.
	/*Application code starts here*/
	while(1)
	{
		/*Do something usefull with debounced_buttons*/
	}

}

void interrupt User_Input()
{
	TMR0L=0x00; /*Not really needed, but lets be explicit*/
	TMR0H=0x00;
	TMR0ON=1;

	Service_user_Input(); /*you may choose to call this in an timed function in the main thread too*/
}

Kenneth Kuhn’s Debounce Code

[Chad] has been using a modified version of [Kenneth Kuhn’s] debounce code for some time now. Small, fast, and well commented, it uses an integrator algorithm.

debounce.c:

/******************************************************************************

debounce.c
written by Kenneth A. Kuhn
version 1.00

This is an algorithm that debounces or removes random or spurious
transistions of a digital signal read as an input by a computer.  This is
particularly applicable when the input is from a mechanical contact.  An

integrator is used to perform a time hysterisis so that the signal must
persistantly be in a logical state (0 or 1) in order for the output to change
to that state.  Random transitions of the input will not affect the output

except in the rare case where statistical clustering is longer than the
specified integration time.

The following example illustrates how this algorithm works.  The sequence
labeled, real signal, represents the real intended signal with no noise.  The

sequence labeled, corrupted, has significant random transitions added to the
real signal.  The sequence labled, integrator, represents the algorithm
integrator which is constrained to be between 0 and 3.  The sequence labeled,

output, only makes a transition when the integrator reaches either 0 or 3.
Note that the output signal lags the input signal by the integration time but
is free of spurious transitions.

real signal 0000111111110000000111111100000000011111111110000000000111111100000

corrupted   0100111011011001000011011010001001011100101111000100010111011100010
integrator  0100123233233212100012123232101001012321212333210100010123233321010
output      0000001111111111100000001111100000000111111111110000000001111111000

I have been using this algorithm for years and I show it here as a code
fragment in C.  The algorithm has been around for many years but does not seem
to be widely known.  Once in a rare while it is published in a tech note.  It

is notable that the algorithm uses integration as opposed to edge logic
(differentiation).  It is the integration that makes this algorithm so robust
in the presence of noise.
******************************************************************************/

/* The following parameters tune the algorithm to fit the particular
application.  The example numbers are for a case where a computer samples a
mechanical contact 10 times a second and a half-second integration time is

used to remove bounce.  Note: DEBOUNCE_TIME is in seconds and SAMPLE_FREQUENCY
is in Hertz */

#define DEBOUNCE_TIME		0.3
#define SAMPLE_FREQUENCY	10
#define MAXIMUM			(DEBOUNCE_TIME * SAMPLE_FREQUENCY)

/* These are the variables used */
unsigned int input;       /* 0 or 1 depending on the input signal */
unsigned int integrator;  /* Will range from 0 to the specified MAXIMUM */
unsigned int output;      /* Cleaned-up version of the input signal */

/* Step 1: Update the integrator based on the input signal.  Note that the
integrator follows the input, decreasing or increasing towards the limits as
determined by the input state (0 or 1). */

  if (input == 0)

    {
    if (integrator > 0)
      integrator--;
    }
  else if (integrator < MAXIMUM)
    integrator++;

/* Step 2: Update the output state based on the integrator.  Note that the
output will only change states if the integrator has reached a limit, either

0 or MAXIMUM. */

  if (integrator == 0)
    output = 0;
  else if (integrator >= MAXIMUM)
    {
    output = 1;
    integrator = MAXIMUM;  /* defensive code if integrator got corrupted */
    }

/********************************************************* End of debounce.c */

Ubi de Feo’s Debounce Code

Arduino sketch to debounce pins from an array.

#define ARRAY_SIZE 8
// array of pins to be debounced
short pinsToDebounce[]={
  2,3,4,5,6,7,8,9
};
// array of pin state
int swStates[]={
  0,0,0,0,0,0,0,0};
// array of previous pin state
int swPrevStates[]={
  0,0,0,0,0,0,0,0};
// array to store the actual state during debounce
int swDebouncedStates[]={
  0,0,0,0,0,0,0,0};
// array to store the previous state during debounce
int swPrevDebounceStates[]={0,0,0,0,0,0,0,0};
// time to debounce
int debounceDelay=100;
// array of previous times the pin has been checked
long prevTimes[]={
  0,0,0,0,0,0,0,0};

void setup(){
  Serial.begin(9600);

 initSwitches();
}
void loop(){
 readSwitches();
}

void initSwitches(){
  for(int i=0;i<ARRAY_SIZE;i++){
    pinMode(pinsToDebounce[i],INPUT);
  }
}
void readSwitches(){

  // Serial.print("active switch set ");
  // Serial.println((int)activeSwitchSet);

  for(short sw=0;sw<ARRAY_SIZE;sw++){
    volatile int pin=pinsToDebounce[sw];
    volatile int mpPin=pin;
    volatile int pinPosition=sw;

    swStates[pinPosition]=digitalRead(pin);
  }

  debouncePins();
   checkStateChange();
}
void debouncePins(){

  volatile long _millis=millis();

  for(short sw=0;sw<ARRAY_SIZE;sw++){
    if(swStates[sw]!=swPrevStates[sw]){
      prevTimes[sw]=_millis;
    }
    if(_millis-prevTimes[sw]>debounceDelay){
      prevTimes[sw]=_millis;
      swDebouncedStates[sw]=swStates[sw];
      /*
      Serial.print("button ");
       Serial.print(sw);
       Serial.print(" is ");
       Serial.println(swDebouncedStates[sw]);
       */
    }
    swPrevStates[sw]=swStates[sw];
  }

}

void checkStateChange(){
  for(short sw=0;sw<5;sw++){
    if(swPrevDebounceStates[sw]!=swDebouncedStates[sw]){
      /*
      Serial.println("");
      Serial.print("button ");
      Serial.print(sw);
      Serial.print(" ");
      Serial.println(swDebouncedStates[sw]);
      */
      if(swDebouncedStates[sw]==1){
        pinActive(sw);
      }
      if(swDebouncedStates[sw]==0){
        pinInactive(sw);
      }
    }
    swPrevDebounceStates[sw]=swDebouncedStates[sw];
  }
}

void printDebStates(){
  Serial.println("%%%%%%%%%%%%%%%%%%%%%%%%");
  for(int i=0;i<ARRAY_SIZE;i++){
    Serial.print(swDebouncedStates[i]);
    Serial.print('*');

  }

  Serial.println("");
}
void pinActive(int _id){

  Serial.print("active pin ");
  Serial.println(pinsToDebounce[_id]);
}
void pinInactive(int _id){
   Serial.print("inactive pin ");
 Serial.println(pinsToDebounce[_id]);
}

Christoph Gommel’s Debounce Code

Debouncing and scanning a keyboard matrix with an STM32 ARM processor.

/*
  keyboard.c
  Used on an STM32F103 in a wireless remote control
  (C) 2010 Christoph Gommel <chg@contronix.de>
  Contronix GmbH, Nizzastr. 6, 01445 Radebeul, Germany
  Tags: matrix, keyboard, stm32, stm32f103, debounce, hackaday
 */
#include "stm32f10x.h"
#include "keyboard.h"
#include "fifo.h"

#define KEYBOARD_GPIO (GPIOA)

// Cols and rows are intentionally placed in the right order enabling the bit shifting trick used.

#define COL1 (GPIO_Pin_1)
#define COL2 (GPIO_Pin_2)
#define COL3 (GPIO_Pin_3)
#define COLS (COL1|COL2|COL3)

#define ROW1 (GPIO_Pin_4)
#define ROW2 (GPIO_Pin_5)
#define ROW3 (GPIO_Pin_6)
#define ROW4 (GPIO_Pin_7)
#define ROW5 (GPIO_Pin_8)
#define ROWS (ROW1|ROW2|ROW3|ROW4|ROW5)
//#define ROWS_B (ROW5)

#define ColNum (3)
#define RowNum (5)

#define ColStart (1)
#define RowStart (4)

//Very useful macro to increment and modulo in one instruction
#define incmod(n,m); n=((n+1)%m);

// Yes, we have a shift key too
#define SHIFT (GPIO_Pin_0)

GPIO_InitTypeDef GPIO_InitStructure;

#define KeyBufferSize (20)

//volatile unsigned char keypad_queue_buffer[KeyBufferSize];

static char keypad_queue_buffer[KeyBufferSize];
fifo_t keyfifo;

void keyboard_init(void)
{
  //Initialize a self made fifo system
  fifo_init(&keyfifo, keypad_queue_buffer, KeyBufferSize);

  //Give clock to the GPIO
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  //Initialize the rows as push-pull-output
  GPIO_InitStructure.GPIO_Pin = ROWS;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(KEYBOARD_GPIO, &GPIO_InitStructure);

  //The cols ar input with pull-up
  GPIO_InitStructure.GPIO_Pin = COLS | SHIFT;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(KEYBOARD_GPIO, &GPIO_InitStructure);

  GPIO_SetBits(KEYBOARD_GPIO, ROWS);

}

volatile unsigned int CurrentRow = 0;
volatile unsigned int lastScan = 0;
volatile unsigned int thisScan = 0;
volatile unsigned int debounceCnt;
volatile unsigned int numberOfKeys;
volatile unsigned int thisScancode;
volatile unsigned int lastScancode;

//Adapt this to you wishes
#define debounceMax (10)

//The following piece of code will be called every 10 ms from a systick interrupt
void keyboard_scan(void)
{
  unsigned int scan;
  scan = ((~GPIO_ReadInputData(KEYBOARD_GPIO)) & COLS) >> ColStart;

  //this is the really interesting core of my matrix scanning algorithm.
  //each key of the matrix is represented in one bit of a 16 bit unsigned short ;)
  thisScan = (thisScan | (scan << (ColNum * RowNum))) >> ColNum;
  //Release Current Row
  GPIO_SetBits(KEYBOARD_GPIO, 1 << (CurrentRow + RowStart));

  incmod(CurrentRow,RowNum);
  //prepare next Row

  GPIO_ResetBits(KEYBOARD_GPIO, 1 << (CurrentRow + RowStart));

  if (0 == CurrentRow)
  {
    //Something changed, let's reset the debounce counter
    if (thisScan != lastScan)
    {
      debounceCnt = 0;
    }
    //...or increase if nothing changed
    else if (debounceCnt < debounceMax)
    {
      debounceCnt++;
    }
    //... if the threshold is reached
    if (debounceCnt == debounceMax)
    {
      numberOfKeys = 0;
      int i;
      //count the number of keys pressed and mark the position of the set bit in "thisScancode"
      for (i = 0; i < (ColNum * RowNum); i++)
        if (thisScan & (1 << i))
        {
          numberOfKeys++;
          thisScancode = i + 1;
        }
      //ignore multiple key presses...
      if (1 != numberOfKeys)
        thisScancode = 0;
      else
      {
       //.. except the shift key which is NOT part of the matrix
        if (keyboard_getshift())
          thisScancode |= KEYBOARD_SHIFT;
        if (thisScancode != lastScancode)
        {
          //printf("Scan: %d\r\n",thisScancode);
          fifo_put(&keyfifo, thisScancode);
        //this is typematic. to generate multiple keystrokes on one single looooooooooong press
#ifdef ENABLE_TYPEMATIC
          typematicCnt=0;
#endif
        }
#ifdef ENABLE_TYPEMATIC
        else
        {
          typematicCnt++;
          if ((typematicThreshold+typematicSpeed)<=typematicCnt)
          typematicCnt=typematicThreshold;

          if (typematicThreshold==typematicCnt)
          {
            fifo_put(&keyfifo, thisScancode);
          }
        }
#endif
      }
      lastScancode = thisScancode;
      //Now we have to do the processing because we get some freshly debounced situation, baby!
      //currentKey=((thisScan^oldScan)&thisScan);
      //oldScan=thisScan;
    }
    lastScan = thisScan;
    thisScan = 0;
  }
}

//that's it, folks ;)

Dean Hall’s Debounce Code

Debouncing a pin interrupt on AVR microcontrollers

/*
 * Sample debouncing code
 *
 * Copyright 2010 Dean Hall.
 * This code snippet is offered under the MIT license:
 * http://www.opensource.org/licenses/mit-license.html
 */

/*
 * The hardware is an AVR with a switch connected to the INT0 input.
 * The firmware uses:
 *
 * - AVR Libc: http://www.nongnu.org/avr-libc/
 * - The AvrX microkernel: http://www.barello.net/avrx/
 *
 * This sample code performs debouncing on the INT0 input.
 * Here is the expected sequence of actions:
 *
 * - The AVR can be doing anything (awake or asleep) when the button is pressed
 * - The button press produces a low-going signal and bounces for a time
 * - The low-going signal activates the INT0 interrupt
 * - The INT0 service routine:
 *     - Disables the INT0 interrupt so bouncing doesn't cause multiple IRQs
 *     - Puts an AvrX message in the event queue of the inputs task
 * - The event in the queue causes the inputs_task to be scheduled, which:
 *     - Performs the action that the INT0 button is supposed to invoke
 *     - Starts an AvrX delay timer to allow for the button to stop bouncing
 *       During this delay, other AvrX tasks may run.
 *     - After the delay, the INT0 interrupt is re-enabled for the next input.
 *
 * In this design, the delay is especially long (1s) due to a very cheap button
 * and no need to respond to fast button presses.
 *
 * The downside of this software design is that no other input events are
 * processed while a button is debounced, although their press-messages are
 * queued up and handled after the debounce period.  This behavior was
 * acceptable for this immediate design, but may not be for all situations.
 */

#include <avr/interrupt.h>
#include "avrx.h"

#define DEBOUNCE_DELAY (uint16_t)(1000 / MILLISECONDS_PER_TIC)

static MessageControlBlock remote_ctrl_pressed_msg;

/* Remote Control button press interrupt service routine */
AVRX_SIGINT(INT0_vect)
{
   IntProlog();

   /* Disable INT0/RemoteCtrl interrupt */
   GICR &= ~_BV(INT0);

   AvrXIntSendMessage(&inputs_msg_queue, &remote_ctrl_pressed_msg);
   Epilog();
}

AVRX_GCC_TASKDEF(inputs_task, TASK_STACK_SIZE, 3)
{
   TimerControlBlock debounce_timer;
   MessageControlBlock *pmsg;

   /* Set INT0/RemoteCtrl and INT1/OpTest to trigger on falling edge */
   GICR &= ~(_BV(INT0) | _BV(INT1));
   MCUCR &= ~(_BV(ISC00) | _BV(ISC10));
   MCUCR |= (_BV(ISC01) | _BV(ISC11));
   GICR |= (_BV(INT0) | _BV(INT1));

   /* ... other initialization stuff removed */

   for (;;)
   {
       /* Wait for a message that an input was pressed or timer expired */
       pmsg = AvrXWaitMessage(&inputs_msg_queue);

       /* ... removed if-cases for other unrelated input messages */

       else if (pmsg == &remote_ctrl_pressed_msg)
       {
           DEBUG_PRINT(VERBOSITY_LOW, "RmtCtrl pressed.\n");

           /* ... removed code that performs the action the button invokes */

           /* Debounce delay for RemoteCtrl button; lets other threads run */
           AvrXDelay(&debounce_timer, DEBOUNCE_DELAY);

           /* Clear flag and enable interrupt */
           GIFR |= _BV(INTF0);
           GICR |= _BV(INT0);
       }
   }
}

Brad Basler’s Debounce Code

Debounce synchronously or asynchronously using an ISR. Usage is discussed in this thread.

debounce.h:

#ifndef __DEBOUNCE_H__
#define __DEBOUNCE_H__

#include <avr/interrupt.h>

//Optimized for 20 millisecond period on a 1Mhz clock
#define DBNC_TIMR0_PRESCALER 	_BV(CS02)
#define DBNC_TIMR0_BASEVAL	 	178

//Defines the size of the debouncer handler arrays (i.e. 4 for a maximum of four)
#define DBNC_NUM_DEBOUNCERS		8

//Defines the number of timer ticks before a value is considered legitimate
//This will define a maximum debounce time of approx 100 milliseconds @ 5
//The minimum debounce time will be approx 80 milliseconds
#define DBNC_COUNTER_TOP 3

#define _BITSHIFTBY(bit,num) ((bit)<<(num))

typedef void(*debounceHandler)(uint8_t,uint8_t);

typedef struct
{
	//A pointer to a volatile port (I/O or otherwise)
	volatile uint8_t *port;

	//A pointer to a debounceHandler function
	debounceHandler handler;

	//This is the decremental counter which determines
	//if the button has been debounced
	uint8_t counter;

	/*

		Bits 0-3: bit index to check against (0-7)
		Bits 4-5: unused
		Bit    5: Last known sate
		Bit    6: Signaled
		Bit    7: Asynchronous

	*/
	uint8_t bitmap;
} DBNC_ITEM;

typedef struct
{

	//An array of debouncer units
	DBNC_ITEM dbUnits[DBNC_NUM_DEBOUNCERS];

	//This is set to 1 when any signal in the dbncSignaled array has been set
	uint8_t signalReady;

} DBNC_GLOBAL;

/*
	Forward Declarations
*/

//ISR for timer0 overflow interrupt
ISR(TIMER0_OVF_vect);
void callSignaledHandlers(void);
void registerDebouncer(volatile uint8_t *port,uint8_t bit,uint8_t index,uint8_t Asynchronous,debounceHandler handler);
void signalChangedState(uint8_t index,uint8_t counterTop);
void initializeDebouncerTimer();

#endif

debounce.c:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "debounce.h"

volatile DBNC_GLOBAL db;

//ISR for timer0 overflow interrupt
ISR(TIMER0_OVF_vect)
{
	uint8_t i;
	uint8_t temp;
	uint8_t workDone = 0;

	//Cycle through all debounced items
	for (i=0;i<DBNC_NUM_DEBOUNCERS;i++)
	{
		//Skip items that have been idle
		if (db.dbUnits[i].counter == 0)
			continue;

		workDone = 1;

		//If debounce period has elapsed
		if (--db.dbUnits[i].counter == 0)
		{

			//Grab current logical bit state of the port (1 or 0)
			temp = (((*(db.dbUnits[i].port)) & _BV((db.dbUnits[i].bitmap & 0b111))) != 0);

			//If current state != last state
			//store change
			if (temp != ((db.dbUnits[i].bitmap & _BV(5)) >> 5))
			{
				//Flip last state bit
				db.dbUnits[i].bitmap ^= _BV(5);

				//If this debouncer item is synchronous
				//Then signal it
				if (!(db.dbUnits[i].bitmap & _BV(7))) {

					//Signal this debouncer item
					db.dbUnits[i].bitmap |= _BV(6);

					//Signaling options
					db.signalReady = 1;
				}

				//Otherwise it's asynchronous,
				//call immediately
				else
					//Call Handler
					(*db.dbUnits[i].handler)(i,temp);
			}
		}
	}

	//If all counters were already 0, disable the timer
	if (!workDone)
		TCCR0B = 0;

	TCNT0  = DBNC_TIMR0_BASEVAL;
}

//Call any signaled handlers (to be executed in main program loop)
void callSignaledHandlers(void)
{
	int i;

	if (!db.signalReady) return;

	for (i=0;i<DBNC_NUM_DEBOUNCERS;i++)
	{
		//Check if this item is signaled
		if (db.dbUnits[i].bitmap & _BV(6)) {

			//If so, reset its signal
			db.dbUnits[i].bitmap &= ~_BV(6);

			//Call item and pass on last known state
			(*db.dbUnits[i].handler)(i,(db.dbUnits[i].bitmap & _BV(5))>>5);
		}
	}

	//Reset signal
	db.signalReady = 0;

}

void registerDebouncer(volatile uint8_t *port,uint8_t bit,uint8_t index,uint8_t Asynchronous,debounceHandler handler)
{
	//Store port pointer
	//Store handler pointer
	//Reset counter to 0
	//Store bitmap of bit offset/asynchronous state
	//Set signaled to 0
	db.dbUnits[index].port 		= port;
	db.dbUnits[index].handler 	= handler;
	db.dbUnits[index].counter  	= 0;

	db.dbUnits[index].bitmap	= _BITSHIFTBY((Asynchronous != 0),7)|
						 		  _BITSHIFTBY((((*port) & _BV(bit)) != 0),5)|
						 		  bit;
}

void signalChangedState(uint8_t index,uint8_t counterTop)
{
	if (!counterTop)
		db.dbUnits[index].counter = DBNC_COUNTER_TOP;
	else
		db.dbUnits[index].counter = counterTop;

	if (!TCCR0B)
		TCCR0B = DBNC_TIMR0_PRESCALER;
}

void initializeDebouncerTimer()
{
	//Note: doesn't start timer
	TCCR0A = 0x00;
	TCCR0B = 0x00;
	TCNT0  = DBNC_TIMR0_BASEVAL;
	TIMSK0 = _BV(TOIE0);
}

test.c:

#include <avr/io.h>
#include <util/delay.h>
#include "debounce.h"

volatile uint8_t lastState;

void ButtonClicker(uint8_t index,uint8_t state);

/*

Pin-change interrupt 0

	Detects pin changes on PCINT0-7 (all masked on), i.e. PB0-7 on Atmega2560

	It compares these values to the last known state and signals a change
	on any pins that have changed state.

*/
ISR(PCINT0_vect)
{
	uint8_t temp = lastState^PINB;
	lastState = PINB;

	if ((temp & _BV(0)))
		signalChangedState(0,2);
	if ((temp & _BV(1)))
		signalChangedState(1,3);
	if ((temp & _BV(2)))
		signalChangedState(2,4);
	if ((temp & _BV(3)))
		signalChangedState(3,5);
	if ((temp & _BV(4)))
		signalChangedState(4,20);
	if ((temp & _BV(5)))
		signalChangedState(5,50);
	if ((temp & _BV(6)))
		signalChangedState(6,200);
	if ((temp & _BV(7)))
		signalChangedState(7,200);
}

int main(void)
{
	//Initialize PORTB as all inputs, no internal pull-ups
	DDRB  = 0x00;
	PORTB = 0x00;

	//Initialize PORTD as all outputs, all HIGH (LEDs off)
	DDRD  = 0xFF;
	PORTD = 0xFF;

	//Initial timer setup (does not start timer)
	initializeDebouncerTimer();

	lastState = PINB;

	//Fills in details regarding
	registerDebouncer(&PINB,PB0,0,1,&ButtonClicker);
	registerDebouncer(&PINB,PB1,1,1,&ButtonClicker);
	registerDebouncer(&PINB,PB2,2,1,&ButtonClicker);
	registerDebouncer(&PINB,PB3,3,1,&ButtonClicker);
	registerDebouncer(&PINB,PB4,4,0,&ButtonClicker);
	registerDebouncer(&PINB,PB5,5,0,&ButtonClicker);
	registerDebouncer(&PINB,PB6,6,0,&ButtonClicker);
	registerDebouncer(&PINB,PB7,7,0,&ButtonClicker);

	//Enable pin-change interrupt & mask
	PCICR  = _BV(PCIE0);
	PCMSK0 = 0xFF;

	//Enable interrupts
	sei();

	while(1)
	{
		//This will loop through any signaled debouncer items and
		//call their handlers (doesn't apply to asynchronous)
		callSignaledHandlers();

		_delay_ms(5);
	}

	return 0;
}

void ButtonClicker(uint8_t index,uint8_t state)
{
	if (state == 0)
	{

		PORTD ^= _BV(index);
	}
}

William Dillon’s Debounce Code

Uses rising and falling edge detection in software to create a digital filter.

/* uint8_t doDebounce(uint8_t *state, volatile uint8_t *port, uint8_t pin)
 *
 * This function implements the logic for detecting button transitions for
 * arbitrary ports and bits.  The return value is the debounced value the the
 * given pin.
 *
 * The implementation of this function is inspired by the digital filter
 * presented in ECE573 at Oregon State University.  It is different because
 * I'm using the single 8-bit variable to store all of the state, rather than a
 * 8 bit accumulator and 8 bit flag.  We're using the range from 0x00 -> 0x7F
 * and bit 7 (0x80) as the flag.
 *
 * The user of this function must provide a static state variable.  This
 * value encodes the state and history of the pin  Failure to maintain the state
 * would cause erratic behavior.  The port can be any of the PINn
 * memory-mapped input buffers.  Pin must be between 0 and 7.
 *
 * Because these buttons pull to ground, we'll invert the meaning of the edges
 * in software, 1 = yes is much more natural.
 */
uint8_t doDebounce(uint8_t *state, volatile uint8_t *port, uint8_t pin) {
       uint8_t old  =  *state & 0x7F;
       uint8_t flag = (*state & 0x80)? 1 : 0;

       // Digital filter part, value = (old * .75) + (new * .25)
       old -= (old >> 2);                                                              // 1 - (1/4) = .75
       old += ((*port) & (1 << pin))? 0x1F : 0x00;             // if bit set, add .25

       // Software schmitt trigger
       // Newly detected rising edge
       if ( (flag == 1) && (old > 0x70) ) {
               flag = 0;
       }

       // Newly detected falling edge
       else if ( (flag == 0) && (old < 0x07) ){
               flag = 1;
       }

       // Update the state variable
       *state = (old & 0x7F) | ((flag & 0x01) << 7);

       // Return only the pin state
       return flag;
}

Mike Szczys’ Debounce Code

I fell in love with the Danni Debounce code early on. Here’s my retouched version of it.

/*--------------------------------------------------------------------------
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
}

Links to other code

Some folks just sent us links to code. Here they are:

[Pieter Conradie’s] matrix scanning code.

[Hernandi F. Krammes’] debounce code for PIC.

[Dave] uses [Jack Ganssle’s] debounce code; Listing 2 of this page.

 

[Photo credit: Jack Ganssle]

38 thoughts on “Debounce Code – One Post To Rule Them All

  1. Err, wait a minute here. You asked for submissions to do a best-of and then didn’t tell anyone you don’t have a way to crowdsource the votes or make sense of all the submissions? Sounds like you may have wasted the time of people contributing.

    And you added a disclaimer that you’re not sure if these actually work? I’m a bit disappointed in this whole thing.

  2. I have to agree with Terry. If I wanted an aggregate of debounce code without any value added I would have googled it.

    Something (anything) extra might have made this post more worth it.

  3. Well i’m just happy my code is at the top (probably becasue it was submitted last)

    Keen to see what people have to say about my code though. Will read through a few other enrties tonight to get some more ideas to improve my code.

  4. @testing1 if you change the moving average window size to 8 or 16 you can get rid of the offending instructions and replace them with shifts (or, better yet, let your optimizing compiler do it for you).

  5. @testing1: I’m not sure how you think his code is going to work without some kind of timer. There is nothing in the code that dictates how often the samples should be taken (except for a constant which isn’t used). So depending on how fast your MCU is running the debounce time will be either slower or faster.

    Unless I’m completely blind and stupid (which at 01:21 isn’t too unlikely).

  6. This is sooo sad to see. The fact that NO micro-controller manufacturer (AFAIK) has ever implemented selectable hardware debounce on at least some pins is inexcusable. A tiny drop of combinational logic on the die would debounce anything in hardware and save the developer a huge amount of time and money. That makes the micro-controller more valuable.

    But nope – instead, the Marketing Wonks at these micro manufacturers spend all their time figuring out how to milk money out of us for development tools.

  7. “We also tried to weed out code that using delay loops for debounce”

    you only have to proof read one or two paragraphs per article try spell check and then read it out loud to see if it makes grammatical sense

  8. Wow! There is some super complicated debounce code there. I’m a bit iffy on the once implementing filters, and all sorts of wild counting algorithms.

    What happened to the good old:
    1. Hook the button up to a scope
    2. Get the mean time for the output to stabilize
    3. Create an input filter that which only accepts the input after it has changed for at least that amount of time.

    If you wanted to be really fancy, you could use statistics to find the time which guarantees to catch 99% of input, with 95% confidence. And even if that 1% got through, it would definitely get detected next cycle.

    For a microprocessors and digital systems assignment using AVR assembly, we came up with a solution that used a timer interrupt set at the denounce time (10ms, well overkill), 4 registers, and 10 lines of assembly. It took 20 ms best case to detect a button press, 30ms worst case, with the system being in sleep mode nearly the entire time.

    Needless to say, but simple is often best.

  9. “Mike Szczys’ Debounce Code” should be titled “Peter Danneger’s Debounce Code”.

    The principle is also called vertical counter, because it uses only a couple of variables (4+ a few helpers) to simultaneously debounce a byte bits (that’s right, 8 switches) that have to be the same state a few times in a row in order to “trickle” through to the bitwise logic into the debounced output variable.

  10. @Drone: I don’t know a whole lot of different controllers, but I know at least TI has implemented configurable input qualification in DSPs and microcontrollers.

    For the C2000 controllers, you can select the sampling interval for each block of 8 inputs, and either 3 or 6 consecutive identical samples before the bit in input register changes.

  11. I really like Thomas Flayols’ submission, only because I’ve never thought of that before! Obviously the *10 /10 can be changed to 16 or 8 or something similar to make it far more efficient. Still, great idea :)

  12. It just occurred to me that there’s different scenarios for de-bouncing. I typically only need to know when a button has been pushed – then I increment a variable, enter a menu, whatever. I only need to debounce so it doesn’t appear that the button is pressed several times in succession. If the code is long, some times I don’t need anything. If it executes fast, I just throw in a delay.

    I never thought about a situation when you need to know how long a button has been pressed, or when it changes states. Still I can’t think of any time you’d see noise on an input pin unless the button just changed state…in which case you can just look for the change and ignore subsequent changes within the stabilization time. You’d only run into a problem when the stabilization time exceeds that of human reaction time…or the repeat rate if we’re talking about a mechanical device. But if that’s the case, you should probably use a better button.

    Am I missing something here? Is there some case where it isn’t so trivial?

  13. I don’t think Thomas Flayols’ Debounce Code will work.

    The line button=(button * 9+INPUT * 10)/10; will not result in more than 1 since button is unsigned char the remainder will be dropped.

    suppose button = 1 and input = 1
    button = (1*9 + 1*10)/10 = 19/10 = 1.9 resulting in 1 in char math.

    1. Hope this helps someone:
      I think what Thomas Flayols wanted to achieve is an exponential filter, no moving average as stated. To circumvent the integer issue he multiplied the incoming values by a factor of 100, not seeing that after optimizing the equation this is not enough.
      So here would be a working exponential filter with a value range [0,100]:

      button = (button * 9 + INPUT * 100)/10;
      if (button > 50)

      Shifting the values instead of multiplication/division might be of no help in speed optimization becase this would lead to a 50% weight instead of the 10% which could still lead to a too rough signal.

  14. If the input generates and interrupt I set a mask bit which is cleared by a timer interrupt a bit later. The time int is used for system timing routines and clearing the mask only takes a line or two of code. Far simpler than any of the examples above. Been working in systems for the last ten years

  15. @DavidG

    Simplicity is not a requirement here. The simplest way is to throw a small delay loop in and lock the microprocessor while waiting for the bounce to finish. But those submissions were weeded out because HaD didn’t deem them to be a useful solution.

    Yet the sad reality is that we have a heck of a lot of projects on the net that do things like flashing a basic lightbulb and they throw an 8MHz processor at it. So what if the processor is monopolised? Just carefully set your interrupt priorities and nothing of value will be missed.

    If Joe average needs to use 100 bytes of program space on a microcontroller that spends the vast majority of it’s life simply either scanning inputs or doing nops while timers are running in the background they may as well be doing nops while waiting for bouncing to end. I’ve used this method in one of my more complicated projects where all my timers were being used for something else.

    I get it, you people think it’s bad programming practice. But here’s a site that a lot of very entry level people read. Not everyone wants to write a 100 line solution when a single line delay_ms(100); will do the job.

    1. and thanks, I’m new in embedded systems and C programming, and delay_ms(100) really helped me, and after I’ll “finish” the code I’ll change it to something more elegant :)

  16. Hi Mike et. al.

    I know it’s old news, but I see that this page is still very popular, so I created a general purpose debouncing module in C that uses a binary state machine and a counter with configurable hysteresis to keep track of the debounced state of a digital input. It also registers and remembers a falling edge event (e.g. “key pressed”) and rising edge event (“key released”):
    http://piconomic.co.za/debouncing-a-digital-input-such-as-a-button/

    Here is a complete, stand-alone example that can be loaded and simulated in Atmel Studio:
    http://piconomic.co.za/download/debounce_example.zip

    Enjoy!
    Pieter

  17. I agree that this is a little bit unnecessary, especially if you’re polling. Even if you’re using interrupts, set a bit somewhere until you’re done executing some bit of code.
    Basically – as soon as the first edge hits, ignore the button for a little while.

  18. I just found this thread. I’d like to add my comment to the hardware debouncing: Some people say:
    1) “It’s bad because it takes up space”: 0402 packaged cap and resistor together takes 1.5mm^2 space. Don’t tell me you don’t have that much free space next to each switch on a PCB.

    Even if you add a logic gate. SOT23 fits into 3x3mm

    2) “It takes time to put it on” Really? I admit it might take an extra second for the components and if you have X thousand units that adds up, but this is negligible when your product sits in a warehouse for weeks and shipping takes weeks as well.

    3) “Adds to BOM”. OK, it does, by how much? You must have at least 20+ components on a PCB anyway. If you’re smart, you can use decoupling caps for debouncing as well.

    4) “Adds cost”: 0201-0402 caps-resistors go for 0.002USD on Farnell, which is not even Shenzhen! If reeled you get 10000 for less than 20USD. If you go nifty and throw in some logic it adds another 0.01USD.

    Conclusion: I admit I’m a HW engineer but: I’m not saying debouncing must be solved with HW or SW. I think that depends on the context of the project but discarding one method just because it’s “uncool” is not a very educated approach regardless what your background is.

Leave a Reply to OJWCancel 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.