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]
Putting a tens of lines of code into a RSS header – splendid!
I am a little disappointed that you haven’t “hacked” a solution to the up / down vote system on a wordpress article, or at least a static page.
This is beautiful. Thanks for putting this all together in one spot.
copy them into a stackoverflow question then anyone can up/down-vote them…
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.
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.
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.
good code
Why do in code what you should do in hardware. 1cap, 1 resistor. Cost.. ~1 cent.
Tweeks
Haters gonna hate.
When it comes down to it, debounce is a pretty simple task. I don’t know that I need so many different code samples for this.
@Tweeks: because it’s one less resistor, and one less capacitor to install.
Because you may need to save space in your design.
Nice to see all this code.
Now someone just has to implement this into the Linux mouse driver.
/me shakes fist at cheap mouse.
I really love Kenneth Kuhn’s code. KiSS!
My vote go to Kenneth Kuhn (if it work…)
kudo for explaining so well and not using timer…
Thomas Flayols seem short at first sight but UGH! a mul? A DIV??? by far the most inefficient.
@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).
… not to mention, the branches in Kuhn’s code could stall the pipeline on a pipelined machine
@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).
@sneakypooHi: You’re right, code should be put into a Timer, I did say it into a comment, hackaday’s guys must revoved it…
Great website and thank’s to have post my code!
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.
“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
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.
Maybe the contest should have been “best way to up/down vote”. Stackoverflow was the first thing that came to my mind, but there may actually be a native WP option.
Most efficient debouncing of multiple inputs in the same time you can achieve only with vertical counters:
http://www.dattalo.com/technical/software/pic/debounce.html
“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.
Even my one is there! thanks hackaday! and is the light one concept!
@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.
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 :)
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?
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.
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.
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
@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.
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 :)
Oof, sure are a lot of angry nerds here! W/E
Thanks Ubi de Feo! Your code really helped me out and is allowing me to move on to other (frankly, more fun) aspects of a system I’m working on.
Much Appreciated.
Thanks a lot ….Since have been searching GOD debounce code from last Now I got it….
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
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.
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.