Debounce for buttons - what's your favorite technique? - Page 1
Forum section: electronics / beginners

author: kanurys
author_rank: Contributor
timestamp: October 08, 2013, 11:12:20 pm
content: Hi guys! It's been a while since I've posted on here. I was wondering what you preferred when considering debounce circuits or code for user interface buttons. I'll start with this:I've given up on capacitors and resistors for this sort of circuit since we now have so many easily programmed micro controllers. I like to store the last time a button was pressed and compare it to the current time like this arduino (C) example because you can hold the button down for several signals sent. (millis() just returns the current run time since last reset)const int debounce_time = 500; //msunsigned long last_press_time = 0; //initialize.........if(debounce_time < millis() - last_press_time && button_is_pressed()){last_press_time = millis();/*......Do stuff.........*/}What do you like to use in your projects?

author: AG6QR
author_rank: Frequent Contributor
timestamp: October 08, 2013, 11:53:19 pm
content: When I was in school learning logic design with discrete gates (back when many commercial products used discrete TTL gates) I liked the old classic technique of using a break-before-make SPDT switch. Connect the center terminal to ground, put weak pull-ups on the two poles, and connect the two poles to the inputs of an SR flip-flop. The output of the flip-flop is very clean, with no bounce whatsoever.You can do a similar technique with a microcontroller, but you need two pins for each switch. Suppose you have the hardware set so that, at most, only one of set_pin or reset_pin can be logic zero. The code is something likeCode:void handle_pin_interrupt(void){static bool pin_state;if(!reset_pin) {pin_state=false;}if(!set_pin) {pin_state=true;}}The disadvantage to either of these techniques is that they require extra hardware, either in the form of microcontroller pins or the form of a flip-flop. Also, they're not good for simple SPST pushbuttons. The advantage is that there are no timing issues to deal with, and the state unambiguously flips one time when the switch contact is made.A timing loop as you've described in the first post is usually more practical with a microcontroller and a pushbutton.

author: Psi
author_rank: Super Contributor
timestamp: October 08, 2013, 11:58:35 pm
content: I prefer to debounce in software.As soon as the button changes state to pressed i perform the action but also set a lockout timer which skips reading the pin again for X msIf you need the action to happen on release rather than press you can just move things around.Sometimes i also set another 'hold' timer when the button is pressed, i then clear it on release (if pressed quick the hold timer never expires).If it does expire then code gets run for a "button hold" event. Usually this part is put inside one of my repeating slow timer events.For example. Holding the button beeps every 500ms and decrements a variable.For soft timers in code i use 0 to mean idle and set them to 1 to "prime" them to start counting.The timer interrupt increments them and sets them back to zero after X increments.If i want them to run forever i have a timer_handler function called from main() which checks for a 0 (idle) state.If idle it runs the timer code and sets the timer variable back to 1 to start again.

author: mariush
author_rank: Super Contributor
timestamp: October 09, 2013, 01:28:40 am
content: It depends on what microcontroller is used.If it's a mcu with low memory and the deboucing routines don't have room, it's easy to do some hardware debouncing by using a capacitor and a resistor. ~ 0.1-0.47uF uf smd capacitors are cheap and small enough to charge quickly and discharge just as fast.Alternatively, a simple inverter with schmitt triggering also works... they cost 3-4 us cents in quantity.

author: free_electron
author_rank: Super Contributor
timestamp: October 09, 2013, 02:17:52 am
content: I use the classic rc network followed by a schmitttrigger, and then postprocess in software.My main thread is fall thru. When a key is detected a flag is set that tells the dispatcher what to do.At the same time a counter is launched that decrements on every pass of the main loop. When the counter hits zero key scanning resumes.

author: Bertho
author_rank: Regular Contributor
timestamp: October 09, 2013, 02:21:43 am
content: High speed interrupt running symmetrical debounce on on/off sides:Code:#define DBCNT 20 // 20ms at 1kHz interrupts#define BUTTON (PORTx & (1<<BUTTONBIT))uint8_t flags;uint8_t debouncecnt;if(~BUTTON ^ flags == 0) // Detect change of statedebouncecnt = DBCNT;if(!--debouncecnt) // Set flag after state stable for some timeflags = ~BUTTON;The code is quite efficient in assembly on small micro controllers with bit set/clr/test.

author: kanurys
author_rank: Contributor
timestamp: October 09, 2013, 02:33:38 am
content: Neat discussion, everyone. I also really like the software implementation because it's easier to make changes or vary the debounce time as necessary. The up-side to hardware solutions like r/c stuff is it saves programming space. (uint8_t) As long as I have space, I'll probably do it there. (<<<that's-what-she-said opportunity)The counter inside a counter technique is a cool idea. I may have to give that a try, if you don't mind.Would anyone be interested in sketching up a diagram of a switch into a schmidt trigger into a digital pin?

author: Bertho
author_rank: Regular Contributor
timestamp: October 09, 2013, 02:41:08 am
content: This is a PIC implementation of the symmetrical counter with change detection with an interrupt running at ~12kHz (therefore counter reset assignment = 0 and is equal to 256):Code:comfPORTA, w; ()xorwf_flags, wandlw(1 << BITS1); if((~PORTA ^ _flags) & (1 << BITS1) == 0)btfscSTATUS, Zclrf_dbs1; _dbs1 = 0decf_dbs1, f; if(!--_dbs1)btfssSTATUS, Zgotono_change_s1; false-clause -> skip to next buttonbsf_flags, BIT_S1; true-clause -> reversed flag: buttons active lowbtfscPORTA, BITS1bcf_flags, BIT_S1; (9, 11)no_change_s1[edit: if the counter needs another value, then a "movlw xxx" can be added after the andlw (which sets the flags) and the "clrf _dbs1" is changed to a "movwf _dbs1"]

author: victor
author_rank: Regular Contributor
timestamp: October 09, 2013, 02:53:57 am
content: I wrote my arduino class but I'm almost sure is not very efficient.

author: ve7xen
author_rank: Super Contributor
timestamp: October 09, 2013, 03:59:31 am
content: I use the last approach describedhereentitled "Handling Mulitple Inputs". The article itself is pretty good as well.The technique is basically to use an array of bytes, where each byte is a sampled input state (of 8 bits). ANDing all the values gives a debounced active-high output. ORing the values can give active-low output.Easy to understand and implement correctly, cheap on code space, pretty fast with comparable memory usage to other techniques, and works well enough. Biggest problem is that edges are delayed until stable, rather than inputs locked out while unstable, which may be undesirable.

author: Rick Law
author_rank: Super Contributor
timestamp: October 09, 2013, 04:17:12 am
content: I prefer to count... Keys are at once every x ms (x<100) and a counter is incremented. Then I can test for how long the key was held down also.if (KeyEnter>1000) { // held down for longest, resetDo what needs done to reset} else if (KeyEnter >100) { // held down for x seconds, means go into config modeif (KeyEnter==2) { // press downEDIT --- initial post was not yet finished but got posted by mistake.... Here is the completed post:I prefer to count. Keys are scanned once every x ms (x<100) and a counter is incremented if press or set to zero if not. Then I can debounce and/or test for how long the key was held down. But it is code-heavy (consumes memory).lastKeyEnter = KeyEnter;if (readTheKey()) {KeyEnter++;} else {KeyEnter=0;}if (KeyEnter>1000) {// held down for about 10 seconds (for my selected x), go do a reset// no need to clear anything since I am reseting} else if (KeyEnter > 200) {// held down for 2 seconds (for my selected x), go into config mode// note, this will be called even if the user intents to hold the key longer for the// reset, so this block of code must keep in mind it could be just a "waypoint".....KeyEnter=lastKeyEnter=0; // indicate key is processed} else if (KeyEnter ==0 && lastKeyEnter>justLongEnough) {// this is executed on key release so it doesn't get done while held down// when it was held down just long enough (10ms, 50ms) whatever that// number depends on the key. For the tact buttons I have, I use around// 20-50ms. Gives me reasonable responsive key and get rid of the bounce... do what is needed ...KeyEnter=lastKeyEnter=0; // indicate key is processed}

author: kanurys
author_rank: Contributor
timestamp: October 09, 2013, 04:32:28 am
content: I've also read an adc (analog) key reading into an array element, incremented every program cycle. Each cycle, I compared the values to the first value and when they were all within a certain tolerance I'd execute the function of the key. I could adjust the tolerance value based on system noise and unstable keys. Increasing the size of the array would also increase the debounce.I primarily use this in conjunction with other methods. It works well for multiple buttons on a single analog pin.

author: mcinque
author_rank: Unknown
timestamp: October 09, 2013, 11:26:19 am
content: My two cents:read the state of an input for at least three times, each delayed of 20msif the stateis the samefor all the three readings(i.e. 1, 1, 1), then do somethingelse (1,0,1) consider it a bounce and do nothingworks for me

author: kanurys
author_rank: Contributor
timestamp: October 09, 2013, 10:47:31 pm
content: Has anyone attempted a "leaning" debounce method which can adjust for desired operation? Any ideas?

author: bookaboo
author_rank: Frequent Contributor
timestamp: October 09, 2013, 11:25:15 pm
content: 1mS timer/interrupt, decrementing a counter from 30 (or whatever) any glitch detected resets the counter to the beginning.Simple but it works.

author: ludzinc
author_rank: Unknown
timestamp: October 09, 2013, 11:51:22 pm
content: I chat about the debouncing I use in a post here:http://ludzinc.blogspot.com.au/2013/07/ive-seen-light.htmlIn a nutshell it's advantage is that it's easily applied to arrays of inputs, is configurable for different states (active low and active high) and can report change in *state*, which is useful for toggle functions when using, erm, toggle switches (as opposed to momentary).I can't take credit - my software buddy gave this code to me.

author: zapta
author_rank: Super Contributor
timestamp: October 10, 2013, 04:29:08 am
content: Quote from: AG6QR on October 08, 2013, 11:53:19 pmOne variation of this is implementing the flip flop with a single buffer, e.g. 4010. Connect a resistor between the input and the output to form the positive feedback. Connect the SPDT to vcc and ground and the center to the buffer's input.If you want to save the resistor and use a SPST switch, you can use one of thesehttp://www.onsemi.com/pub_link/Collateral/MC14490-D.PDFHad to implement denouncing once in FPGA/VHDL. Instead of changing the state on first input change, I used a counter and changed state to 1 (0) only after N consecutive input 1's (0's). More immune to noise this way.

author: zapta
author_rank: Super Contributor
timestamp: October 10, 2013, 04:38:54 am
content: Quote from: Bertho on October 09, 2013, 02:21:43 amDon't you want a 'else' before the last 'if'? (no need to decrement and test the counter if you just set it to DBCNT).Also, you can't you take off the two '~' ?Changing state after having N stable readings looks right to me.

author: miceuz
author_rank: Frequent Contributor
timestamp: October 10, 2013, 11:20:23 am
content: Code:typedef struct {uint8_t pinBuffer;uint8_t pressed;uint8_t lastState;} switch_t;void debounce(volatile switch_t *button, volatile uint8_t *port, uint8_t pin){button->pinBuffer = (button->pinBuffer) << 1;button->pinBuffer |= ((*(port) & _BV(pin)) != 0);if(0 == button->pinBuffer) {button->pressed = 1;} else if(0xFF == button->pinBuffer){button->pressed = 0;}}void serviceButton(volatile switch_t *button, void (*onPressed)(void), void (*onReleased)(void)) {if(button->pressed != button->lastState) {if(button->pressed) {if(0 != onPressed) {onPressed();}} else {if(0 != onReleased) {onReleased();}}button->lastState = button->pressed;}}i keep the debounce routine in a timer interrupt - this way you have control of responsiveness of GUI. As you can see, the button is considered pressed/released when it has same state in 8 consecutive reads.then in my main routine I call serviceButton which acts if the button was pressed/released:Code:void onUpButtonPressed() {//do stuff}void onUpButtonReleased() {//do stuff}//......while(1) {serviceButton(&upButton, onUpButtonPressed, onUpButtonReleased);}

author: geraldjhg
author_rank: Regular Contributor
timestamp: October 14, 2013, 07:01:25 pm
content: generally i use some interrupt routine that cheks the state of the pushbuttonsevery 5 ms or so, keep an image in software and compare it to next checkswitches that remain in a fixed position are best avoided, see cell fonesand remote control etc etc

author: kanurys
author_rank: Contributor
timestamp: October 14, 2013, 07:26:15 pm
content: That's like the opposite of debounce. ^^

author: Bertho
author_rank: Regular Contributor
timestamp: October 14, 2013, 07:50:25 pm
content: Quote from: zapta on October 10, 2013, 04:38:54 amThe "else" may be there. It is not mandatory in the code and you just have DBCNT one higher at start. The code was written specifically to be easier in assembly by not having both true and false cases (see other post). Also, in that case DBCNT is zero so that a "clrf" instruction could be used.The ~ is there because the buttons are active low. The code was copied from one of my projects and not really cleaned up. It was part of a replacement firmware for the Ikea Dioder (https://github.com/openspaceaarhus/ikea-sign) to be used as a side-lit sign (.http://www.vagrearg.org/content/skilt20).

author: MauriceS
author_rank: Contributor
timestamp: October 17, 2013, 04:35:44 am
content: I like the software solution published in Pt 2 form Jack Ganssle:http://www.ganssle.com/debouncing.htmIt is simple to implement, supports multiple buttons, and works like a charm...

author: Lukas
author_rank: Frequent Contributor
timestamp: October 17, 2013, 04:51:56 am
content: http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29This one does 8 keys and support long press and autorepeat.

author: Kryoclasm
author_rank: Regular Contributor
timestamp: October 17, 2013, 09:14:25 am
content: Let the air out of the button. No more bouncing.

author: Rufus
author_rank: Super Contributor
timestamp: October 17, 2013, 11:52:46 am
content: My favorite is posted in thishttp://www.microchip.com/forums/tm.aspx?m=426510thread.

author: madires
author_rank: Super Contributor
timestamp: October 17, 2013, 12:58:47 pm
content: Before designing any debounce hardware/software check the bouncing characteritics of the push button. Checking the button every 1ms as someone suggested is total overkill. Most buttons work well with 20 to 30ms, i.e. the bouncing happens within that timeframe, but there are also some buttons which require a longer delay. Waiting 500ms might prevent pressed-by-mistake situations but gives a bad feedback to the user ("I'm pressing that damn button for ages but nothing happens."). Since most buttons don't give a tactile response the UI has to provide the feedback, typically a change of the display and a beep optionally (could be annoying).

author: AG6QR
author_rank: Frequent Contributor
timestamp: October 17, 2013, 08:04:16 pm
content: Quote from: madires on October 17, 2013, 12:58:47 pmI agree with all of that, especially the part about making your debounce technique fit the button.But the idea of waiting to process a button got my attention.Usually, with a typical normally open push button, if it has been "open" for a long time, and then you register a "closed" condition, even for a microsecond, you can be absolutely sure the button was pressed. There's no need to wait at all to start processing the button. However, you want to wait some time period for things to settle down before you start interpreting subsequent openings and closings as genuine.But the reverse isn't true. If the button has been closed for a while, a detected opening should probably remain open for some time before you interpret it as a real opening, and not just a glitch in the contacts.