## Efficient button reading algorithm - best practice question.

42 posts / 0 new
Author
Message

Again I'm looking for advice on the most efficient algorithm or the best practice for accomplishing a task. In this case it is debouncing a set of push buttons and setting a bit in a state variable if the button has been pressed. The design is for a navigator type keypad with up, down, left, right, and center momentary pushbuttons. The algorithm uses a timer interrupt to check the pin state every 5 mS and if the pin is set for 5 consecutive intervals 25mS then we say that it really is pressed and properly debounced.

In words:
Timer interrupt checks pin state every 5 mS.
If button is released and the count for that button is > 0 decrement that button's count.
If button is pressed and the count for that button is < max, increment that button's count.
If button is pressed and the count for that button is >= max then set button state to pressed and set that button count to 0.
Note that we do not set the button state to released "“ that is done by the user code once the button press has been processed.

And the defines and interrupt used are:

```// This gives us 25mS debounce
#define MAX_COUNT 5

// Define bit positions for each of the buttons
#define LFT 0
#define RGT 1
#define UP 2
#define DWN 3
#define CTR 4

volatile uint8_t nav_state;
volatile uint8_t lft_count;
volatile uint8_t rgt_count;
volatile uint8_t up_count;
volatile uint8_t dwn_count;
volatile uint8_t ctr_count;

//Timer2 Compare A interrupt vector handler
ISR(TIMER2_COMPA_vect)
{
if(nav_lft())
{
if( lft_count < MAX_COUNT ) lft_count++;
else
{
bit_set(nav_state,LFT);
lft_count = 0;
}
}
else // not pressed
{
if (lft_count > 0) lft_count--;
}

if(nav_rgt())
{
if( rgt_count < MAX_COUNT ) rgt_count++;
else
{
bit_set(nav_state,RGT);
rgt_count = 0;
}
}
else // not pressed
{
if (rgt_count > 0) rgt_count--;
}

if(nav_up())
{
if( up_count < MAX_COUNT ) up_count++;
else
{
bit_set(nav_state,UP);
up_count = 0;
}
}
else // not pressed
{
if (up_count > 0) up_count--;
}

if(nav_dwn())
{
if( dwn_count < MAX_COUNT ) dwn_count++;
else
{
bit_set(nav_state,DWN);
dwn_count = 0;
}
}
else // not pressed
{
if (dwn_count > 0) dwn_count--;
}

if(nav_ctr())
{
if( ctr_count < MAX_COUNT ) ctr_count++;
else
{
bit_set(nav_state,CTR);
ctr_count = 0;
}
}
else // not pressed
{
if (ctr_count > 0) ctr_count--;
}
}
```

So can anyone suggest a much better (either faster or less code or both) to do this?

Smiley

Here's how I read the 5 buttons on the MT128

```char bt,br,bl,bb,bc;
//--------------------------
char kphit(void){
//return true iff any key pressed on 5 button keypad
char ok;

ok=(PINA & 0x1f) != 0x1f;
return ok;
}

//----------------------
char getkp(void){
char cp;

while(!kphit()){};
cp=PINA & 0x1f;
return cp;
}

char cp,cpl,cpos;
//-----------------------------
void butloop(void){
char c;

cprintf("button loop  q to quit\n");
cprintf("kp cp t r b l c\n");
while(c != 'q'){
if(kbhit()){      //if char waiting from pc
c=getchar();
}
if(kphit()){      //any key pressed?
cp=getkp();     //0x1f=none     0x0f 0x17 0x1b 0x1d 0x1e=one of 5
cpos=cp != cpl;
cpl=cp;
if(cpos){
boop();
}
bt=(cp & 0x01)==0;
br=(cp & 0x08)==0;
bb=(cp & 0x10)==0;
bl=(cp & 0x02)==0;
bc=(cp & 0x04)==0;
cprintf("pct d  pct 02x pct d pct d pct d pct d pct d\r",kphit(),cp,bt,br,bb,bl,bc);
_StackCheck();
}
delnms(50);
_StackCheck();
}
}

```

Imagecraft compiler user

Do you really need to process buttons separately? That is, after two counts of the UP button pressed, you need to start debouncing the now-pressed LEFT button separately?

I would simply use the 5-count rule for ANY non-zero buttons state. That will debounce every combination of one or more buttons. Any time the state changes in any fashion during the debounce period, start the 5-count over.

Here's an outline of how I'd do it (less any stupid mistakes)

```timer_isr()
{
static u8 db_cnt = 0;
static u8 last_keys = 0;

if (keys != 0)
{
if (keys != last_keys)
{
db_cnt = 5;
last_keys = keys;
}
else if (db_cnt != 0)
{
if (--db_cnt == 0)
Global_keys = keys;
}
}
}```

This can very easily be extended to add a first-repeat delay and then subsequent repeat delays, which is what I usually do. Just load the new delay where Global_keys gets set, and keep a flag that says whether this is the first delay or subsequent delays, to determine which delay to load.

Bob:
I'm wanting to record the button presses in the background and deal with them elsewhere. What you are doing is polling and you could miss a button press if you are doing something else when a button is pressed

kk6gm:
I thought about that but then thought that maybe a glitch in one button might show up on the fifth count of an actual button being pressed so both that button and the glitch button would be recorded as debounced and valid.

Smiley

smileymicros wrote:
So can anyone suggest a much better (either faster or less code or both) to do this?

Use static instead of volatile for counters, and move them to begining of ISR():

```ISR(TIMER2_COMPA_vect)
{

static uint8_t lft_count;
static uint8_t rgt_count;
static uint8_t up_count;
static uint8_t dwn_count;
static uint8_t ctr_count;

...

```

Faster and less code.

... and show the definition of the function nav_lft().

smileymicros wrote:
kk6gm:
I thought about that but then thought that maybe a glitch in one button might show up on the fifth count of an actual button being pressed so both that button and the glitch button would be recorded as debounced and valid.

That wouldn't happen because then (keys != last_keys) would be true, so the debounce sequence would start all over. Assuming a 1-tick glitch, the debounce sequence would then start over again the next tick, when keys != last_keys would be true again.

Dondu_ wrote:
... and show the definition of the function nav_lft().

#define nav_lft() !bit_get(NAV_LFT_PINX,NAV_LFT_PIN)

And bit_get is:
#define bit_get(p,m) ((p) & bit(m))

Tomorrow I'll compile your version and look at the size of the results.

Thanks,
Smiley

Sure enough, I found one mistake. last_keys needs to be set even in the zero (no keys pressed) condition, or else if the same key is pressed twice it won't be recognized the 2nd time.

```timer_isr()
{
static u8 db_cnt = 0;
static u8 last_keys = 0;

if (keys != 0)
{
if (keys != last_keys)
db_cnt = 5;
else if (db_cnt != 0)
{
if (--db_cnt == 0)
Global_keys = keys;
}
}
last_keys = keys;
}
```

Peter Daneggers (danni) debounce algorithm perhaps? Only counts to four, though...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

This is how I have done it for upto 8 buttons. The handler is called by a timer every 10ms. It can flag different activities to be done on a make or a break or both. The button reporting can be handy for debugging.

```
//*******************************************************//
//*		      PUSH BUTTON STATE MACHINE  V1.1			*//
//*         Supports  1-N buttons        				*//												*/
//*      Written by   Lee de Vries                      *//													*/
//*		              April 2009						*//
//*******************************************************//
#include  		//essential include
#include 	//supports storing strings & arrays in flash
#include 			//enables standard input/output
#include "buttons.h"
#define BUTTONS 8

extern volatile uint16_t secs_up,msecs_up;

int BUTTONS_handler(void)
{
void report_button(uint8_t,uint8_t);	//associated funtion prototype
static uint8_t pb_states=0;		//push button state

for(uint8_t button=0;button```

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

I always like the shift register method if you only have a couple of buttons.

```button_db_state<<=1;
if (is_button_down())
{
button_db_state|=1;
}

button_db_state&=0x3F;

if (button_state==0x3F)
{
if (button_pressed==0)
{
button_pressed=1;
button_changed_flag=1;
}
}
else if (button_state==0x00)
{
if (button_pressed==1)
{
button_pressed=0;
button_changed_flag=1;
}
}
```

And since only 5 counts are needed and a byte can hold 8 bits, also the debounced button state can be stored in the shift register.

You would need one byte per button though.

And the biggest question is how to debounce depends on your needs, do the buttons need to be separate or can they be debounced all at once until reading is stable. For example the big red STOP button on a heavy machinery must be separate from other user pushable interface buttons so that understanding the STOP button is independent of user/ESD events on rest of the UI buttons.

Once again the question arises: Are you trying to find a fast/tight/smart solution or are you looking for a solution that a reader will easily understand? They may not be the same thing.

If you put my routine in a 5ms interrupt and save the button state booleans in an array, you're done.

Imagecraft compiler user

I'm surprised that no-one has mentioned Ganssle's work:

http://www.ganssle.com/debouncin...

Leon Heller G1HSM

I thought smiley would know about it.

The shift register method I posted is mentioned in Ganssle's work.

I have written a small library using that method, with auto-repeat and long/short push detection.

clawson wrote:
Once again the question arises: Are you trying to find a fast/tight/smart solution or are you looking for a solution that a reader will easily understand? They may not be the same thing.

Danni's is, after I walked you through it. :mrgreen:

Added value: After understanding it, you are also certified to be in good control of bitwise opertaions in C.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Quote:

Danni's is, after I walked you through it

Kind of my point - it took an expert to be able to even explain it's operation to other experts and it even took a software simulation to do this. That probably does not make great fodder for a magazine article aimed at newbies ;-)

Quote:
Timer interrupt checks pin state every 5 mS.
If button is released and the count for that button is > 0 decrement that buttonâ€™s count.
If button is pressed and the count for that button is < max, increment that buttonâ€™s count.
If button is pressed and the count for that button is >= max then set button state to pressed and set that button count to 0.

I think I spot a weirdness here:

So, I press the button and it bounces back and forth a few time, incrementing and resetting is done as described above.

Then it comes to rest, and eventually we get to the point where count >= max, so we set buttonState to pressed and reset the counter. Now, I have not released the button yet, and let's assume that the main thread "consumes" the pressed state and resets the state to 'not pressed'. The counter is still incrementing and will go = max once again in 25 ms, and the state will once again be set to pressed.

You have auto-repeat with a very short initial delay (25 ms), and a quite high repeat frequency (40 Hz). Is this the intention?

-----

Again, Joe: Have you looked at Peter Daneggers deboune code? It has been up for discussion here several times, and the last time around I put in some effort in revealing it's inner workings down to every bitwize operation. I would say it is compact both re code size and execution time. It does 8 buttons for exactly the same costs as one button. There are variants which do controlled auto repeat. You can easily, with very little cost, do button releases as well.

Lee Theusch has a "shift-based solution" that I have not dug that deep into but which he claims has similar performance/efficiency.

Whenever a debouncing thread goes on long and deep enough he usually links to the thread where "the big battle" between these two stood. If he passes by he might do that again.

I'll try to locate the thread where I dissected Dannis algorithm.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

My walkthrough is in this specific post: https://www.avrfreaks.net/index.p... , and there are amendments in posts following that one.

To give you a feel for the compactness of Dannis debouncer, here it my variant with all comments and stuff removed. The bare metal, if you like. For an explanation of this intricate stuf, see the walkthrough in that other thread. The code below debounces n switches in parallell, where n is the bit-width of the variables, i, ct0, ct1 etc. So, if they are uint8_t's then it debounces 8 switches.

```void ISR(void)
{
i = key_state ^ KEY_INPUT;
ct0 = ~( ct0 & i );
ct1 = ct0 ^ (ct1 & i);
i &= ct0 & ct1;
key_state ^= i;
key_press |= key_state & i;
}```

If I find the time tonight, I'll build and present the disassembly for it.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Thanks for the links Johan, I'll look at them today.

clawson wrote:
Quote:

Danni's is, after I walked you through it

Kind of my point - it took an expert to be able to even explain it's operation to other experts and it even took a software simulation to do this. That probably does not make great fodder for a magazine article aimed at newbies ;-)
Yeah, it is going to take some deep thinking to get my mind around Danni's algorithm.

leon_heller wrote:
I'm surprised that no-one has mentioned Ganssle's work:

http://www.ganssle.com/debouncin...

This is where I got the 25mS from (Ganssle says 20 to 50mS). My algorithm is similar to one of his, but a bit clearer IMHO. I haven't compared code size.

clawson wrote:
Once again the question arises: Are you trying to find a fast/tight/smart solution or are you looking for a solution that a reader will easily understand? They may not be the same thing.
I'd like to think that when there is an algorithm that is significantly better - either much smaller and/or faster - then it is possible to explain it to a patient learner. For the final library, I'll use the 'best' that I can find and then do my best explaining it. I think Danni's algorithm may be good for a bonus section after one has absorbed the basics of bitwise operators this can show them how clever some solutions are and the need to look for other folks algorithms - thank God for the Internet - since there is a good bet that somebody somewhere has done it better than you. This is my experience anyway.

Smiley

Quote:

I'd like to think that when there is an algorithm that is significantly better - either much smaller and/or faster - then it is possible to explain it to a patient learner.

In which case I would concentrate on danni's (perhaps Lee's?). There really have probably been hundreds of threads on the subject and I don't think anyone's ever come back disappointed after being pointed in the direction or Peter's or Lee's code so they probably do good bet that they represent the pinnacle of "somebody somewhere has done it better".

(I'm guessing quite a lot of folks may actually use it as a "black box", plumb it in, it works, retire satisfied).

Not a real ISR (yet), but an ordinary function (e.g. a RET instead of an IRET) but the generated code for the debouncing per se can be seen below. AVR-GCC as in WinAVR-20100110 (2nd ed), optimization -Os.

```00000186 :

void ISR(void)
{
static uint8_t ct1, ct0, key_state;
uint8_t i;
i = key_state ^ PINA;
186:	30 91 0e 01 	lds	r19, 0x010E
18a:	29 b3       	in	r18, 0x19	; 25
18c:	23 27       	eor	r18, r19
ct0 = ~( ct0 & i );
18e:	80 91 0f 01 	lds	r24, 0x010F
192:	82 23       	and	r24, r18
194:	80 95       	com	r24
196:	80 93 0f 01 	sts	0x010F, r24
ct1 = ct0 ^ (ct1 & i);
19a:	90 91 10 01 	lds	r25, 0x0110
19e:	92 23       	and	r25, r18
1a0:	98 27       	eor	r25, r24
1a2:	90 93 10 01 	sts	0x0110, r25
i &= ct0 & ct1;
1a6:	82 23       	and	r24, r18
1a8:	89 23       	and	r24, r25
key_state ^= i;
1aa:	98 2f       	mov	r25, r24
1ac:	93 27       	eor	r25, r19
1ae:	90 93 0e 01 	sts	0x010E, r25
key_press |= key_state & i;
1b2:	98 23       	and	r25, r24
1b4:	80 91 11 01 	lds	r24, 0x0111
1b8:	89 2b       	or	r24, r25
1ba:	80 93 11 01 	sts	0x0111, r24
}
1be:	08 95       	ret```

27 CLKs (for the 6 statements in the function body) if AVR Simulator does not lie to me.

So, to debounce 8 pins this uses 4 statically allocated bytes and 27 CPU clocks (plus ISR entry/exit execution time).

Good enough, Joe?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

I still want to go through this with a pencil and paper computer to make sure I fully understand what is going on, but till then, thanks for the input.

Smiley (uh... Joe)

To understand the working, you should run the demo code on the STK500:

https://www.avrfreaks.net/index.p...

Peter

Quote:

I still want to go through this with a pencil and paper computer to make sure I fully understand what is going on, but till then, thanks for the input.

If you where the young and impatient noob, you'd just go over and read my walk-through. It is an elaboration of my first paper-and-pen exercise with Dannis algorithm during a boring conference.

EDIT: I once had a go at drawing a schematic of logic gates that would be the equivalent of Dannis solution (for one pin). Dannis software solution is just eight (or whatever) such stacked aside each other. While I got to a sketch with to many crossing signals I never really got it nice and clean though. Maybe some other time..

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

JohanEkdahl wrote:
EDIT: I once had a go at drawing a schematic of logic gates that would be the equivalent of Dannis solution (for one pin).

This was exact the way, how I developed this code.

I draw the schematic and then I reduced the count of gates (logical operators) and wires (registers).
Then I translated it into code.

I must look, if I can found this original schematic.

Peter

Ha! Full circle it went, then...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Where's the VHDL?

Well that burned out a few brain cells but I think I've got it. Vertical counting is pretty cool once you get it. Just to make sure I got it, it is only good for counts up to 7-bits since using full 8-bit counts would have the same number of variables as just using one uint8_t for each counter?

```i = key_state ^ PINA ;
if(keypress != i)
{
// the code
}```

Make it even more efficient? Is there any reason to run the algorithm if no bits have changed?

Smiley

That's how I do it. (Since '74. Might have Ganssle beat. He's good at math though. Or is that Crenshaw?)

Imagecraft compiler user

Quote:

Is there any reason to run the algorithm if no bits have changed?

Not sure I'm getting you, Joe.. Even if all pins are the same as the last time around you need to run the algorithm, or you would not count up at all when the switch actually enters the steady state.

Quote:
Well that burned out a few brain cells but I think I've got it.

Yup, that is how I felt after an hour with pencil-and-paper at that boring conference.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

https://www.avrfreaks.net/index.p...

On the Attachment you can see the schematic of the debouncer.

Peter

## Attachment(s):

Last Edited: Fri. Oct 28, 2011 - 11:18 AM

To the average guy, this thread must sound like the encabulator

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

LDEVRIES wrote:
To the average guy, this thread must sound like the encabulator
That was hilarious, had me in tears.

Smiley

danni wrote:
On the Attachment you can see the schematic of the debouncer.

Peter

This reminded me of when I used to do logic designs for Altera chips and would do logic reduction that sort of amazed me. I'd start off with some fairly complex logic that made sense to me, reduce it and have a schematic like the one you showed that sometimes I could barely follow but they seemed to work just fine. I haven't thought about applying this to C programming but it makes sense and may even be worth digging out some old texts to brush up on it if I really am looking for the most efficient algorithms.

Smiley

Cliff wrote "Peter's or Lee's "
http://en.wikipedia.org/wiki/Peters_and_Lee

Four legs good, two legs bad, three legs stable.

My deja-vu sensor just triggered again:

https://www.avrfreaks.net/index.p...

Crap! Shows how bad my memory is getting these days! Must get round to replacing those old 6116s soon...

Four legs good, two legs bad, three legs stable.

clawson wrote:
My deja-vu sensor just triggered again:

https://www.avrfreaks.net/index.p...

And two posts down is Lee's post with links to some substantial battles, uhhmmm... discussions.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]